Преобразование XML в JSON с Python LXML - PullRequest
0 голосов
/ 07 июля 2019

По сути, я хочу преобразовать и xml в json , используя python3 и библиотеку lxml . Здесь важно то, что я хочу сохранить все текст , хвосты , теги и порядок в xml. Ниже приведен пример того, что должна делать моя программа:

Что у меня

<root>
   <tag>
      Some tag-text<subtag>Some subtag-text</subtag> Some tail-text
   </tag>
</root>

Что я хочу (python dict / json)

{
  "root":{
    "tag":[
        {"text":"Some tag-text"},
        {"subtag":{"text":"Some subtag-text"}},
        {"text":"Some tail-text"}
      ]
  }
}

Это просто очень упрощенный пример. Файлы, которые мне нужно преобразовать, намного больше и имеют больше вложений.

Кроме того, я не могу использовать для этого библиотеку xmltodict, только lxml.

Я почти на 99% уверен, что есть какой-то элегантный способ сделать это рекурсивно, но до сих пор я не смог написать решение, которое работает так, как я хочу.

Большое спасибо за помощь

РЕДАКТИРОВАТЬ: Почему этот вопрос не является дубликатом Преобразование XML в JSON с использованием Python?

Я понимаю, что не существует такой вещи, как один к одному , отображающей из xml в json. Я специально спрашиваю, как сохранить порядок текста, как в примере выше.

Кроме того, использование xmltodict не достигает этой цели. F.eg, преобразование xml из приведенного выше примера с xmltodict приведет к следующей структуре:

root:
    tag:
        text: 'Some tag-text Some tail-text'
        subtag: 'Some subtag-text'

Вы можете видеть, что хвостовая часть "Некоторый текст хвоста" была объединена с "Некоторый текст тега"

спасибо

Ответы [ 2 ]

1 голос
/ 08 июля 2019

Я думаю, что если вам нужно сохранить порядок документов (который вы называете «текстовым порядком»), XSLT - хороший вариант. XSLT может выводить простой текст, который может быть загружен как json. К счастью, lxml поддерживает XSLT 1.0 .

Пример ...

Ввод XML (input.xml)

<root>
    <tag>
        Some tag-text<subtag>Some subtag-text</subtag> Some tail-text
    </tag>
</root>

XSLT 1.0 (xml2json.xsl)

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
  <xsl:output method="text"/>
  <xsl:strip-space elements="*"/>

  <xsl:template match="*">
    <xsl:if test="position() != 1">, </xsl:if>
    <xsl:value-of select="concat('{&quot;',
      local-name(),
      '&quot;: ')"/>
    <xsl:choose>
      <xsl:when test="count(node()) > 1">
        <xsl:text>[</xsl:text>
        <xsl:apply-templates/>
        <xsl:text>]</xsl:text>
      </xsl:when>
      <xsl:otherwise>
        <xsl:apply-templates/>
      </xsl:otherwise>
    </xsl:choose>
    <xsl:text>}</xsl:text>
  </xsl:template>

  <xsl:template match="text()">
    <xsl:if test="position() != 1">, </xsl:if>
    <xsl:value-of select="concat('{&quot;text&quot;: &quot;', 
      normalize-space(), 
      '&quot;}')"/>
  </xsl:template>

</xsl:stylesheet>

Python

import json
from lxml import etree

tree = etree.parse("input.xml")

xslt_root = etree.parse("xml2json.xsl")
transform = etree.XSLT(xslt_root)

result = transform(tree)

json_load = json.loads(str(result))

json_dump = json.dumps(json_load, indent=2)

print(json_dump)

Для справки вывод xslt (result):

{"root": {"tag": [{"text": "Some tag-text"}, {"subtag": {"text": "Some subtag-text"}}, {"text": "Some tail-text"}]}}

Вывод на печать из Python (после load () / dumps ()):

{
  "root": {
    "tag": [
      {
        "text": "Some tag-text"
      },
      {
        "subtag": {
          "text": "Some subtag-text"
        }
      },
      {
        "text": "Some tail-text"
      }
    ]
  }
}
0 голосов
/ 10 июля 2019

Вот альтернатива «@Daniel Haley's» решению

def recu(root):
    my=[]
    if root.text:
        my.append({"text":root.text})
    if len(root):
        for elem in root:
            my=my+[recu(elem)]
            if elem.tail:
                my=my+[{"text":elem.tail}]
    my = my[0] if len(my)==1 else my
    return {root.tag:my}
...