使用Python,除一个元素外,按字母顺序排序XML



我正在尝试按字母顺序排序我的XML,同时确保特定元素停留在顶部。我设法按字母顺序对其进行排序,但是我无法让该元素留下来。这是我到目前为止所拥有的:

from lxml import etree
data = """
<Example xmlns="http://www.example.org">
    <E>
        <A>A</A>
        <B>B</B>
        <C>C</C>
    </E>
    <B>B</B>
    <D>D</D>
    <A>A</A>
    <C>C</C>
    <F>F</F>
</Example>
"""
doc = etree.XML(data,etree.XMLParser(remove_blank_text=True))
for parent in doc.xpath('//*[./*]'):
    parent[:] = sorted(parent,key=lambda x: x.tag)
print etree.tostring(doc,pretty_print=True)

从中的结果是:

<Example xmlns="http://www.example.org">
  <A>A</A>
  <B>B</B>
  <C>C</C>
  <D>D</D>
  <E>
    <A>A</A>
    <B>B</B>
    <C>1</C>
  </E>
  <F>F</F>
</Example>

无论如何,我可以停止<E></E>零件及其内容移动吗?

您至少可以以两种方式处理它。您可以对所有内容进行排序,然后通过自定义排序功能将<E>强加到顶部。另外,您可以将要分类的元素拆分为排序,然后将它们附加到非分组元素的末尾。

自定义排序:

使用渐进代码点进行文本排序。您可以使用ord()获取单个字符的代码点。最低的印刷字符是标签。因此,要进行排序,我们可以告诉Python正常对所有元素进行排序,除非tag<E>,然后使用tab进行排序,该分类将首先进行排序。

有一些额外的代码可以处理命名空间。

doc = etree.XML(data,etree.XMLParser(remove_blank_text=True))
ns = doc.nsmap
for parent in doc.xpath('//*[./*]'):
    parent[:] = sorted(parent,key=lambda x: x.tag if x.tag!='{'+ns[None]+'}E' else 't')
print(etree.tostring(doc,pretty_print=True).decode('ascii'))
<Example xmlns="http://www.example.org">
  <E>
    <A>A</A>
    <B>B</B>
    <C>C</C>
  </E>
  <A>A</A>
  <B>B</B>
  <C>C</C>
  <D>D</D>
  <F>F</F>
</Example>

拆分,申请,组合

在这里,我们将父级分为两个列表,对第二个列表进行整合,然后将其合并。

doc = etree.XML(data,etree.XMLParser(remove_blank_text=True))
ns = doc.nsmap
for parent in doc.xpath('//*[./*]'):
    to_sort = (e for e in parent if e.tag!='{'+ns[None]+'}E')
    non_sort = (e for e in parent if e.tag=='{'+ns[None]+'}E')
    parent[:] = list(non_sort) + sorted(to_sort, key=lambda e: e.tag)
print(etree.tostring(doc,pretty_print=True).decode('ascii'))
<Example xmlns="http://www.example.org">
  <E>
    <A>A</A>
    <B>B</B>
    <C>C</C>
  </E>
  <A>A</A>
  <B>B</B>
  <C>C</C>
  <D>D</D>
  <F>F</F>
</Example>

它可以使用以下方式工作,但是似乎无法达到简单的标签,因此它使用长标签,包括 xmlns part:

doc = etree.XML(data,etree.XMLParser(remove_blank_text=True))
    for parent in doc.xpath('//*[./*]'):
        parent[:] = sorted(parent,
                           key=lambda x: (not x.tag =='{http://www.example.org}E', x.tag))
    print(etree.tounicode(doc,pretty_print=True))

此代码将输出:

<Example xmlns="http://www.example.org">
  <E>
    <A>A</A>
    <B>B</B>
    <C>C</C>
  </E>
  <A>A</A>
  <B>B</B>
  <C>C</C>
  <D>D</D>
  <F>F</F>
</Example>
   </Example>n'

以下代码只是输出这些长标签以了解它们的外观:

doc = etree.XML(data,etree.XMLParser(remove_blank_text=True))
    for parent in doc.xpath('//*[./*]'):
        for item in parent:
            print(item.tag)
    {http://www.example.org}E
    {http://www.example.org}B
    {http://www.example.org}D
    {http://www.example.org}A
    {http://www.example.org}C
    {http://www.example.org}F
    {http://www.example.org}A
    {http://www.example.org}B
    {http://www.example.org}C

另一种方法是使用辅助功能解析标签以使其更可读:

def normalize(name):
    if name[0] == "{":
        uri, tag = name[1:].split("}")
        return tag
    else:
        return name
doc = etree.XML(data, etree.XMLParser(remove_blank_text=True))
for parent in doc.xpath('//*[./*]'):
    parent[:] = sorted(parent,
                       key=lambda x: (not normalize(x.tag) == 'E', x.tag))

最新更新