我目前正在努力创建一个简单的python脚本,该脚本允许我解析一个XML文件,以获得特定标记的路径.

Input data

我需要解析的XML文件通常如下所示:

<?xml version="1.0" encoding="utf-8"?>
<Document>
    <Engineering version="V17"/>
    <DocumentInfo>
        <ExportSetting>WithDefaults, WithReadOnly</ExportSetting>
    </DocumentInfo>
    <SW.Blocks.GlobalDB ID="0">
        <AttributeList>
            <AutoNumber>true</AutoNumber>
            <HeaderVersion>0.1</HeaderVersion>
            <Interface>
                <Sections>
                    <Section Name="Static">
                        <Member Name="foo" Datatype="Struct" Remanence="NonRetain" Accessibility="Public">
                            <Member Name="foo1" Datatype="Bool"></Member>
                            <Member Name="foo2" Datatype="Struct">
                                <Member Name="foo2.1" Datatype="Bool"></Member>
                                <Member Name="foo2.2" Datatype="Int"></Member>
                            </Member>
                        </Member>
                        <Member Name="bar" Datatype="Struct" Remanence="NonRetain" Accessibility="Public">
                            <Member Name="bar" Datatype="Bool"></Member>
                            <Member Name="bar 1" Datatype="Bool"></Member>
                            <Member Name="bar3" Datatype="Bool"></Member>
                        </Member>
                    </Section>
                </Sections>
            </Interface>
        </AttributeList>
    </SW.Blocks.GlobalDB>
</Document>

Expected output

我只关心这<Member>个标签,特别是它们的Name个属性.我想做的是遍历所有这些标记,并获得所有底层子 node (即所有<Member>个 node 本身没有子 node )的列表,作为一个XML路径列表(仍然只关注Member个标记).

因此,我希望从给定的示例文件中获得以下结果:

foo.foo1
foo.foo2."foo2.1"
foo.foo2."foo2.2"
bar.bar
bar."bar 1"
bar.bar3

What I tried so far

我编写的第一个脚本基于minidom和一个getElementsByTagName循环,它 for each Member在XML体系 struct 中找到它的级别进行计算,并将其与关联的Name值放在一个列表中. 之后使用第二个循环对该列表进行迭代,并通过级别比较将路径连接起来. 如果它工作了,我会保留那个解决方案(因为他们说,如果它工作了,它并不愚蠢……),但它的行为不正确(根据两个元素之间的级别差异,它计算出正确的路径……或者不).

我以为使用递归循环执行作业(job)会更有效率,但我正在苦苦挣扎.根据StackOverflow上针对类似问题提供的解决方案,我只是写道:

from xml.etree import ElementTree as ET

def find_rec(node, element):
    for item in node.findall(element):
        yield item
        for child in find_rec(item, element):
            yield child.attrib['Name']

print(".".join(find_rec(ET.parse("in.xml"), './Member')))

而且它根本不起作用.运行该脚本仅输出一个空格字符,没有任何消息或错误. 用Member替换./Member不会改变任何事情,在第二个循环中只写yield child也不会改变任何事情.

我try 了使用minidom的其他实现,但都没有成功.

有谁能帮我修复这个代码(并解释为什么它不工作……)?

非常感谢!

推荐答案

以下是仅使用内置模块xml.etree的解决方案:

from xml.etree import ElementTree as et

def findpaths(tree, path):
    for node in tree:
        path.append(node)
        if node.tag == "Member" and not len(node): # Member node without any child
            yield list(path)
        yield from findpaths(node, path) # recurse
        path.pop()

def processpath(path):
    for node in path:
        if node.tag == "Member" and "Name" in node.attrib:
            yield escape(node.attrib["Name"]) # only capture Name for the result

def escape(name):
    if any(c in name for c in (".", " ")): # list of characters worth escaping
        return f'"{name}"'
    return name

tree = et.parse("./source.xml") # assuming your xml is there
for path in findpaths(tree.getroot(), []):
    print(".".join(processpath(path)))

输出:

foo.foo1
foo.foo2."foo2.1"
foo.foo2."foo2.2"
bar.bar
bar."bar 1"
bar.bar3

Python相关问答推荐

如何避免Chained when/then分配中的Mypy不兼容类型警告?

如何使用html从excel中提取条件格式规则列表?

可变参数数量的重载类型(args或kwargs)

如何找到满足各组口罩条件的第一行?

部分视图的DataFrame

使用NeuralProphet绘制置信区间时出错

考虑到同一天和前2天的前2个数值,如何估算电力时间序列数据中的缺失值?

如何更改groupby作用域以找到满足掩码条件的第一个值?

LocaleError:模块keras._' tf_keras. keras没有属性__internal_'''

如何创建引用列表并分配值的Systemrame列

PYTHON、VLC、RTSP.屏幕截图不起作用

如何合并具有相同元素的 torch 矩阵的行?

使用SQLAlchemy从多线程Python应用程序在postgr中插入多行的最佳方法是什么?'

每次查询的流通股数量

如何根据一定条件生成段id

为什么在Python中00是一个有效的整数?

Django抛出重复的键值违反唯一约束错误

Pandas:使列中的列表大小与另一列中的列表大小相同

将Pandas DataFrame中的列名的长文本打断/换行为_STRING输出?

利用广播使减法更有效率