我有一个遗留的文件格式,我正在将其转换为XML进行处理。结构可以概括为:
<A>
<A01>X</A01>
<A02>Y</A02>
<A03>Z</A03>
</A>
标签的数字部分可以从01到99,并且可能存在间隙。作为处理的一部分,某些记录可能添加了其他标记。处理完成后,我通过追踪树将文件转换回传统格式。文件相当大(约150,000个节点)。
这方面的一个问题是,某些使用传统格式的软件假定标签(或转换时的字段)将按字母顺序排列,但默认情况下,新标签会添加到标签的末尾。然后导致它们以错误的顺序从迭代器中出来的分支。
每次添加新标记时,我都可以使用xpath根据标记名称查找前面的兄弟,但我的问题是在导出之前是否有一种更简单的方法对树进行排序?
编辑:
我想我总结了结构。
记录可以包含如上所述的几个级别,以提供类似的内容:
<X>
<X01>1</X01>
<X02>2</X02>
<X03>3</X03>
<A>
<A01>X</A01>
<A02>Y</A02>
<A03>Z</A03>
</A>
<B>
<B01>Z</B02>
<B02>X</B02>
<B03>C</B03>
</B>
</X>
答案 0 :(得分:17)
可以编写一个辅助函数来在正确的位置插入一个新元素,但是如果不了解更多关于结构的信息,很难使它成为通用的。
以下是在整个文档中对子元素进行排序的简短示例:
from lxml import etree
data = """<X>
<X03>3</X03>
<X02>2</X02>
<A>
<A02>Y</A02>
<A01>X</A01>
<A03>Z</A03>
</A>
<X01>1</X01>
<B>
<B01>Z</B01>
<B02>X</B02>
<B03>C</B03>
</B>
</X>"""
doc = etree.XML(data,etree.XMLParser(remove_blank_text=True))
for parent in doc.xpath('//*[./*]'): # Search for parent elements
parent[:] = sorted(parent,key=lambda x: x.tag)
print etree.tostring(doc,pretty_print=True)
产量:
<X>
<A>
<A01>X</A01>
<A02>Y</A02>
<A03>Z</A03>
</A>
<B>
<B01>Z</B01>
<B02>X</B02>
<B03>C</B03>
</B>
<X01>1</X01>
<X02>2</X02>
<X03>3</X03>
</X>
答案 1 :(得分:4)
你可以像这样对xml元素进行排序:
from operator import attrgetter
from lxml import etree
root = etree.parse(xmlfile)
children = list(root)
sorted_list = sorted(children, key=attrgetter('tag'))
如果运行速度太慢,您可能只需对标记名称进行排序并使用xpath获取节点:
tag_list = [item.tag for item in root]
sorted_taglist = sorted(tag_list)
答案 2 :(得分:0)
谷歌搜索 XML 排序器,我到了这里。基于@MattH的工作,我做了一个更完整和可调的功能:
#!python3
from lxml import etree
import sys
if len(sys.argv) < 3:
print("usage : xml_sorted.py file_in.xml file_out.xml")
exit(0)
filename_in=sys.argv[1]
filename_out=sys.argv[2]
def getSortValue(elem):
if isinstance(elem,etree._Comment):
# sort comment by its content
return elem.text
else:
# sort entities by tag and then by name
return elem.tag + elem.attrib.get('name','')
doc=etree.parse(filename_in)
for parent in doc.xpath('//*[./*]'): # Search for parent elements
parent[:] = sorted(parent,key=lambda x: getSortValue(x))
with open(filename_out,"wb") as file:
file.write(etree.tostring(doc,pretty_print=True))