Python xml.etree.ElementTree 'findall()'方法不适用于多个命名空间



我正在尝试解析一个具有多个名称空间的XML文件。我已经有了一个生成名称空间映射的函数——一个带有名称空间前缀和名称空间标识符的字典(代码中的示例(。但是,当我将此字典传递给findall()方法时,它只适用于第一个命名空间,但如果XML路径上的元素在另一个命名空间中,则不返回任何内容。

(它只适用于第一个以None为前缀的命名空间。(

这是一个代码示例:

import xml.etree.ElementTree as ET
file - '.folderexample_file.xml' # path to the file
xml_path = './DataArea/Order/Item/Price' # XML path to the element node
tree = ET.parse(file)
root = tree.getroot()
nsmap = dict([node for _, node in ET.iterparse(exp_file, events=['start-ns'])])
# This produces a dictionary with namespace prefixes and identifiers, e.g.
# {'': 'http://firstnamespace.example.com/', 'foo': 'http://secondnamespace.example.com/', etc.}
for elem in root.findall(xml_path, nsmap):
# Do something

编辑:根据mzjn的建议,我将包含示例XML文件:

<?xml version="1.0" encoding="utf-8"?>
<SampleOrder xmlns="http://firstnamespace.example.com/" xmlns:foo="http://secondnamespace.example.com/" xmlns:bar="http://thirdnamespace.example.com/" xmlns:sta="http://fourthnamespace.example.com/" languageCode="en-US" releaseID="1.0" systemEnvironmentCode="PROD" versionID="1.0">
<ApplicationArea>
<Sender>
<SenderCode>4457</SenderCode>
</Sender>
</ApplicationArea>
<DataArea>
<Order>
<foo:Item>
<foo:Price>
<foo:AmountPerUnit currencyID="USD">58000.000000</foo:AmountPerUnit>
<foo:TotalAmount currencyID="USD">58000.000000</foo:TotalAmount>
</foo:Price>
<foo:Description>
<foo:ItemCode>259601</foo:ItemCode>
<foo:ItemName>PORTAL GUN 6UBC BLUE</foo:ItemName>
</foo:Description>
</foo:Item>
<bar:Supplier>
<bar:SupplierID>4474</bar:SupplierID>
<bar:SupplierName>APERTURE SCIENCE, INC</bar:SupplierName>
</bar:Supplier>
<sta:DeliveryLocation>
<sta:RecipientID>103</sta:RecipientID>
<sta:RecipientName>WARHOUSE 664</sta:RecipientName>
</sta:DeliveryLocation>
</Order>
</DataArea>
</SampleOrder>

根据Jan Jaap Meijerink的回答和mzjn在该问题下的评论,解决方案是在XML路径中插入以名称空间为前缀的名称空间。这可以通过插入通配符{*}作为mzjn的注释和这个答案来完成(https://stackoverflow.com/a/62117710/407651)建议。

要记录解决方案,您可以在代码中添加以下简单操作:

xml_path = './DataArea/Order/Item/Price/TotalAmount'
xml_path_splitted_to_list = xml_path.split('/')
xml_path_with_wildcard_prefix = '/{*}'.join(xml_path_splitted_to_list)

如果有两个或多个节点具有相同的XML路径但名称空间不同,findall()方法(非常自然(会访问所有这些元素节点。

您应该在xml_path中指定名称空间,例如:./foo:DataArea/Order/Item/bar:Price。它使用空名称空间的原因是因为它是默认名称空间,您不必在路径中指定该名称空间。

最新更新