使用 Python 对 XML 进行排序时忽略某些节点

Ignore certain nodes when sorting XML with Python

提问人:TheDataPanda 提问时间:6/29/2023 最后编辑:larsksTheDataPanda 更新时间:6/29/2023 访问量:28

问:

我有一个 XML。粗略地说,有根节点。然后,在该级别之下,您有各种节点,其中一些节点带有“系统”标签。在每个“系统”节点中,还有各种其他节点,其中一些称为“扩展”。最后,在“Extents”中,有各种节点,其中一个是“LineSize”,其“Value”属性设置为一个数字。

我基本上是在尝试对每个“系统”节点进行排序,以便所有“范围”都按“LineSize”排序。我想忽略“系统”中的所有其他节点,而只是按顺序对“范围”进行排序。理想情况下,将其他节点保留在原处(它们总是在所有“范围”之前或之后)。

这是我的代码:

import xml.etree.ElementTree as ET

my_tree = ET.parse('test.xml')
root = my_tree.getroot()

for node in root:
    if node.tag == "System":
        for child_node in node:

            child_node[:] = sorted(child_node, key=lambda segment_node: int(segment_node.find("LineSize").attrib["Value"]), reverse=True)

my_tree.write('test_ordered.xml')

现在的问题是它不会为“Extents”以外的元素找到“LineSize”,因此会抛出错误。

XML 与此类似。运行工作代码后的唯一变化是 id = 1 的扩展将排在 id = 2 的扩展之后,因为我将按降序排序(两者都仍在 name = aa 的系统下。

图形节点将停留在同一个位置,或者至少不会成为排序的一部分。

<root>
 <system name = "aa">
  <graphics>
  </graphics>
  <extents id = "1" name = "aa">
   <LineSize Value = "10" />
  </extents>
  <extents id = "2" name = "aa">
   <LineSize Value = "40" />
  </extents>
 </system>
 <system name = "bb">
  <extents id = "3" name = "bb">
   <LineSize Value = "90" />
  </extents>
  <extents id = "4" name = "bb">
   <LineSize Value = "20" />
  </extents>
  <random1>
  </random1>
  </system>
 <random>
 </random>
</root>
python-3.x xml 解析

评论

0赞 TheDataPanda 6/29/2023
我添加了一个例子
0赞 TheDataPanda 6/29/2023
@mzjn 你认为这可能吗?我基本上只是尝试根据子节点的值进行排序
0赞 larsks 6/29/2023
那不是有效的XML(缺少一个)。我已经纠正了它,但总的来说,最好确保你在问题中包含的任何代码或数据在语法上都是正确的(因为语法问题会分散我们看待真正问题的能力)。"
0赞 larsks 6/29/2023
此外,示例文档与您的代码不匹配。它不包含任何元素(注意大写)。<System>
0赞 TheDataPanda 6/29/2023
谢谢。XML只是快速组合在一起,因为我无法与真正的机密数据共享实际的XML。

答:

0赞 larsks 6/29/2023 #1

下面是一个可能的解决方案:

import xml.etree.ElementTree as ET

my_tree = ET.parse("test.xml")
root = my_tree.getroot()

for node in root:
    if node.tag == "system":
        child_nodes = sorted(
            node,
            key=lambda node: int(node.find("LineSize").get("Value", 0))
            if node.tag == "extents"
            else 0,
        )
        for child_node in reversed(child_nodes):
            node.remove(child_node)
            node.insert(0, child_node)

my_tree.write("test_ordered.xml")

我们获取每个元素的子节点列表,然后对列表进行排序,放置不在列表顶部的元素。对于每个子节点,我们首先将其从之前占据的任何位置移除,然后重新插入。<system>extents.LineSize<extents>

对示例数据运行,这将生成:

<root>
  <system name="aa">
    <graphics></graphics>
    <extents id="1" name="aa">
      <LineSize Value="10" />
    </extents>
    <extents id="2" name="aa">
      <LineSize Value="40" />
    </extents>
  </system>
  <system name="bb">
    <random1></random1>
    <extents id="4" name="bb">
      <LineSize Value="20" />
    </extents>
    <extents id="3" name="bb">
      <LineSize Value="90" />
    </extents>
  </system>
  <random></random>
</root>

评论

0赞 TheDataPanda 6/29/2023
谢谢。我尝试更新它,以便它会以相反的方式排序(最高优先)。但是,似乎会导致一些问题。其中一个扩展数据块最终位于系统节点之外。
0赞 TheDataPanda 6/29/2023
尽管这可能是 VS Code 格式化的问题。当我有 reverse = True 时,它看起来可能正在工作
0赞 larsks 6/29/2023
如果你想要最高,就用而不是(这实际上是我首先测试的,但我假设你首先想要最低,所以我添加了调用)。这会将非节点放在底部而不是顶部,但如果这是一个问题,您可以在调用中使用大值而不是默认值。for child node in child_nodesfor child node in reversed(child_nodes)reversed()<extents>0sorted()