我正在处理一个XML文件,其中包含两种类型的节点(此处为foo
和bar
),如下所示:
<foo>
<id>123</id>
<name>The first foo</name>
</foo>
<foo>
<id>456</id>
<name>The second foo</name>
</foo>
<bar>
<name>The first bar</name>
<foo>123</foo>
</bar>
<bar>
<name>The second bar</name>
<foo>123</foo>
</bar>
请注意:
foo
一个 bar
; bar
都有一个或多个 foo
。我想为每个bar
得到相应的foo
节点,所以我写了这段代码:
import xml.etree.ElementTree as ET
root = ET.fromstring(data)
for bar in root.findall('bar'):
for foo in root.findall('foo'):
if foo.find('id').text == bar.find('foo').text:
foo_of_bar = foo
pass
print bar.find('name').text + ': ' + foo_of_bar.find('name').text
结果:
The first bar: The first foo
The second bar: The first foo
但我问自己是否有更好的方法,使用XPath语法或更pythonic代码。
答案 0 :(得分:1)
因此,如果我理解正确,每个ID都至少有一个foo
,以及一个或多个bar
。
因此,组织此方法的有效方法是使用以下字典:
{
id: (foo, (bar, bar, ...)),
id: (foo, (bar, bar, ...)),
...
}
(或者你可以将foo作为密钥)。
显然你必须寻找所有的foos来获得第一个元素。从那里,您可以对bar[foo='{id}']
的XPath表达式进行查找,该表达式搜索具有bar
子项的foo
元素,这些子项具有引号之间的完整内容。所以:
root = ET.fromstring(data)
foo_bars = {}
for foo in root.findall('foo'):
foo_id = foo.find('id').text
bars = tuple(root.findall("bar[foo='{}']".format(foo_id)))
foo_bars[foo_id] = (foo, bars)
# Do something with foo (one element),
# bars (a tuple of elements) and
# foo_id (A str of the id)
for foo_id, f_bs in foo_bars.items():
foo, bars = f_bs
# Also do something
这避免了迭代每个bar元素的每个foo元素,就像每个foo元素一样,你只迭代必要的bar元素。
或者,您可以迭代一次并在找到元素时构建字典。对于较小的xml文件,这会慢得多,但如果你有一个较大的文件,它可能会更快。
import xml.etree.ElementTree as ET
import collections
root = ET.fromstring(data)
foo_bars = collections.defaultdict(lambda: [None, []])
for child in root:
if child.tag == 'foo':
# Found a new id
foo_bars[child.find('id').text][0] = child
elif child.tag == 'bar':
foo_bars[child.find('foo').text][1].append(child)
else:
# Possibly raise a ValueError?
pass
for foo_id, f_bs in foo_bars.items():
foo, bars = f_bs
# Do something