使用 python lxml 将 xml 转换为 json
Transforming xml to json with python lxml
基本上,我想使用 [ 将 xml 转换为 jsonpython3 和 lxml-库。这里重要的是,我想保留所有 text、tails、tags 和 顺序的xml。下面是我的程序应该能够做什么的示例:
我有什么
<root>
<tag>
Some tag-text<subtag>Some subtag-text</subtag> Some tail-text
</tag>
</root>
我想要的(pythondict/json)
{
"root":{
"tag":[
{"text":"Some tag-text"},
{"subtag":{"text":"Some subtag-text"}},
{"text":"Some tail-text"}
]
}
}
这只是一个非常简单的例子。我需要转换的文件更大并且嵌套更多。
此外,我不能为此使用 xmltodict 库,只能使用 lxml.
我几乎 99% 确定有一些优雅的方法可以递归地执行此操作,但到目前为止我还没有能够编写出一个按我想要的方式工作的解决方案。
非常感谢您的帮助
编辑:为什么这个问题不是 Converting XML to JSON using Python?
的重复
我知道不存在从 xml 到 json 的 一对一 映射。我特别要求一种像上面示例中那样保留文本顺序的方法。
此外,使用 xmltodict 也无法实现该目标。 F.eg,将上面示例中的 xml 转换为 xmltodict 将产生以下结构:
root:
tag:
text: 'Some tag-text Some tail-text'
subtag: 'Some subtag-text'
你可以看到,尾部 "Some tail text" 与 "Some tag-text"[=17= 连接在一起]
谢谢
我认为如果您需要保留文档顺序(您引用的 "text-order"),XSLT 是一个不错的选择。 XSLT 可以输出可以加载为 json 的纯文本。还好lxml supports 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('{"',
local-name(),
'": ')"/>
<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('{"text": "',
normalize-space(),
'"}')"/>
</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(在 loads()/dumps() 之后)的打印输出是:
{
"root": {
"tag": [
{
"text": "Some tag-text"
},
{
"subtag": {
"text": "Some subtag-text"
}
},
{
"text": "Some tail-text"
}
]
}
}
这是“@Daniel Haley”解决方案的替代方案
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}
基本上,我想使用 [ 将 xml 转换为 jsonpython3 和 lxml-库。这里重要的是,我想保留所有 text、tails、tags 和 顺序的xml。下面是我的程序应该能够做什么的示例:
我有什么
<root>
<tag>
Some tag-text<subtag>Some subtag-text</subtag> Some tail-text
</tag>
</root>
我想要的(pythondict/json)
{
"root":{
"tag":[
{"text":"Some tag-text"},
{"subtag":{"text":"Some subtag-text"}},
{"text":"Some tail-text"}
]
}
}
这只是一个非常简单的例子。我需要转换的文件更大并且嵌套更多。
此外,我不能为此使用 xmltodict 库,只能使用 lxml.
我几乎 99% 确定有一些优雅的方法可以递归地执行此操作,但到目前为止我还没有能够编写出一个按我想要的方式工作的解决方案。
非常感谢您的帮助
编辑:为什么这个问题不是 Converting XML to JSON using Python?
的重复我知道不存在从 xml 到 json 的 一对一 映射。我特别要求一种像上面示例中那样保留文本顺序的方法。
此外,使用 xmltodict 也无法实现该目标。 F.eg,将上面示例中的 xml 转换为 xmltodict 将产生以下结构:
root:
tag:
text: 'Some tag-text Some tail-text'
subtag: 'Some subtag-text'
你可以看到,尾部 "Some tail text" 与 "Some tag-text"[=17= 连接在一起]
谢谢
我认为如果您需要保留文档顺序(您引用的 "text-order"),XSLT 是一个不错的选择。 XSLT 可以输出可以加载为 json 的纯文本。还好lxml supports 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('{"',
local-name(),
'": ')"/>
<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('{"text": "',
normalize-space(),
'"}')"/>
</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(在 loads()/dumps() 之后)的打印输出是:
{
"root": {
"tag": [
{
"text": "Some tag-text"
},
{
"subtag": {
"text": "Some subtag-text"
}
},
{
"text": "Some tail-text"
}
]
}
}
这是“@Daniel Haley”解决方案的替代方案
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}