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

时间:2017-09-06 16:34:02

标签: python xml sorting alphabetical

我尝试按字母顺序对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>部分及其内容移动吗?

2 个答案:

答案 0 :(得分:1)

它可以通过以下方式工作,但似乎无法访问简单标记,因此它使用长标记,包括xmlns部分:

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))

答案 1 :(得分:1)

您可以通过至少两种方式处理此问题。您可以对所有内容进行排序,然后通过自定义排序功能强制SHOW GRANTS到顶部。此外,您可以拆分要排序的元素,对它们进行排序,并将它们附加到未排序元素的末尾。

自定义排序:

使用渐进式代码点对文本进行排序。您可以使用<E>获取单个字符的代码点。选项卡最低的打印字符。因此,对于排序,我们可以告诉python正常排序所有元素,除非ord()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>