【问题标题】:Python3 parse XML into dictionaryPython3 将 XML 解析成字典
【发布时间】:2016-10-11 03:56:47
【问题描述】:

看来原帖太含糊了,所以我缩小了这篇文章的重点。我有一个 XML 文件,我想从中提取特定分支的值,但我很难理解如何有效地导航 XML 路径。考虑下面的 XML 文件。有几个<mi> 分支。我想存储某些分支的<r> 值,而不是其他分支。在此示例中,我想要 counter1 和 counter3 的 <r> 值,而不是 counter2。

<?xml version="1.0"?>
<?xml-stylesheet type="text/xsl" href="Data.xsl" ?>
<!DOCTYPE mdc SYSTEM "Data.dtd">
<mdc xmlns:HTML="http://www.w3.org/TR/REC-xml">
<mfh>
<vn>TEST</vn>
<cbt>20140126234500.0+0000</cbt>
</mfh>
<mi>
    <mts>20140126235000.0+0000</mts>
    <mt>counter1</mt>
    <mv>
        <moid>DEFAULT</moid>
        <r>58</r>
    </mv>
</mi>
<mi>
    <mts>20140126235000.0+0000</mts>
    <mt>counter2</mt>
    <mv>
        <moid>DEFAULT</moid>
        <r>100</r>
    </mv>
</mi>
<mi>
    <mts>20140126235000.0+0000</mts>
    <mt>counter3</mt>
    <mv>
        <moid>DEFAULT</moid>
        <r>7</r>
    </mv>
</mi>
</mdc>

由此我想用以下内容构建一个元组: ('20140126234500.0+0000', 58, 7) 其中 20140126234500.0+0000 取自 &lt;cbt&gt;,58 取自 &lt;mi&gt; 元素的 &lt;r&gt; 值(具有 &lt;mt&gt;counter1&lt;/mt&gt;),7 取自 &lt;mi&gt; 元素(具有 &lt;mt&gt;counter3&lt;/mt&gt;)。

我想使用xml.etree.cElementTree,因为它似乎是标准的,并且应该能够满足我的目的。但是我在导航树和提取我需要的值时遇到了困难。以下是我尝试过的一些方法。

try:
    import xml.etree.cElementTree as ET
except ImportError:
    import xml.etree.ElementTree as ET

tree = ET.ElementTree(file='Data.xml')
root = tree.getroot()
for mi in root.iter('mi'):
    print(mi.tag) 
    for mt in mi.findall("./mt") if mt.value == 'counter1':
        print(mi.find("./mv/r").value) #I know this is invalid syntax, but it's what I want to do :)

从伪代码的角度来看,我想做的是:

find the <cbt> value and store it in the first position of the tuple.
find the <mi> element where <mt>counter1</mt> exists and store the <r> value in the second position of the tuple.
find the <mi> element where <mt>counter3</mt> exists and store the <r> value in the third position of the tuple.

我不清楚何时使用element.iter()element.findall()。此外,在函数中使用XPath 或提取我需要的信息时,我运气不佳。

谢谢, 生锈

【问题讨论】:

  • 发布您尝试过的内容。顺便说一句,没有订购常规的dict。根据您的绘图软件,您可能需要维护两个并行列表或用于排序的元组列表。
  • ......无论如何,一个情节计数器名称如何。你想要 值吗?
  • 我已经尝试了几件事,但大多数情况下我什至不知道如何在 Python 中进行操作。我会尝试发布一些我到目前为止所做的事情。是的,我想绘制 r 值。并感谢有关替代方法的建议。这就是为什么我包含有关想要绘制它的信息的原因。我不确定我是否以正确的方式使用字典。如果您能发布一些有关如何使用元组的详细信息,我将不胜感激。
  • 我更新了帖子以缩小焦点并按照 tdelaney 的建议合并元组而不是字典。

标签: python xml elementtree


【解决方案1】:

开始于:

import xml.etree.cElementTree as ET  # or with try/except as per your edit

xml_data1 = """<?xml version="1.0"?> and the rest of your XML here"""
tree = ET.fromstring(xml_data)  # or `ET.parse(<filename>)`
xml_dict = {}

现在tree 具有 xml 树,xml_dict 将是您尝试获取结果的字典。

# first get the key & val for 'cbt'
cbt_val = tree.find('mfh').find('cbt').text
xml_dict['cbt'] = cbt_val

计数器在'mi':

for elem in tree.findall('mi'):
    counter_name = elem.find('mt').text            # key
    counter_val = elem.find('mv').find('r').text   # value
    xml_dict[counter_name] = counter_val

此时,xml_dict 是:

>>> xml_dict
{'counter2': '100', 'counter1': '58', 'cbt': '20140126234500.0+0000', 'counter3': '7'}

一些缩写,虽然可能不那么可读:for elem in tree.findall('mi'): 循环中的代码可以是:

xml_dict[elem.find('mt').text] = elem.find('mv').find('r').text
# that combines the key/value extraction to one line

或者更进一步,构建xml_dict 只需两行,首先是计数器,然后是cbt

xml_dict = {elem.find('mt').text: elem.find('mv').find('r').text for elem in tree.findall('mi')}
xml_dict['cbt'] = tree.find('mfh').find('cbt').text

编辑:

From the docs, Element.findall() 仅查找带有标签且是当前元素的直接子元素的元素。

find() 只找到第一个直接子代。

iter() 递归遍历所有元素。

【讨论】:

  • 谢谢!这看起来非常有用。我很快就会测试它。
  • 我必须将您示例中的 for 循环更改为 for elem in tree.find('md').findall('mi'): 否则,它将找不到任何元素。这是预期的吗? findall 不是递归的吗?编辑 - 我看到你实际上已经回答了。它只查找当前元素的直接子元素。
  • 我在您的示例 xml 中没有看到任何名为“md”的节点/标签。当你做ET.fromstring(xml_data) 时,already gives you the root,在这种情况下是'mdc'。对于ET.parse(&lt;filename&gt;),您需要然后执行tree.getroot(),这将为您提供'mdc'。你的for 循环的tree.find('md') 部分为你做了这些。顺便说一句,见How does accepting an answer work? & What should I do when someone answers my question?
  • 你能推荐一种好方法吗,以便只将某些计数器添加到字典中?例如,只有 counter1 和 counter3。我意识到我提供的样本没有我原始文件的 md 标签。
  • 如果你的计数器被称为'counter',那么在xml_dict[counter_name] = counter_val之前放if counter_name.startswith('counter')。 2 行版本也一样。
猜你喜欢
  • 2021-04-11
  • 2017-09-21
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多