Python:如果没有命名空间添加新元素,xpath.find()将找不到新元素

时间:2013-06-07 09:18:50

标签: python xml xpath python-2.7 xml-namespaces

今天我偶然发现了xml.domxpath模块的特殊行为,我花了一段时间才发现它与XML命名空间有关:

from xml.dom import minidom
import xpath

zooXml = """<?xml version="1.0" encoding="utf-8"?>
<Zoo xmlns='http://foo.bar/zoo'>
  <Compound><Chimp/></Compound>
</Zoo>"""

mydom = minidom.parseString(zooXml)
compound = xpath.findnode('/Zoo/Compound', mydom)
print compound.toxml() # as expected: <Compound><Chimp/></Compound>
print xpath.find("Chimp", compound) # as expected: [<DOM Element: Chimp at 0x24c0cc8>]

到目前为止一切顺利,但如果我现在添加另一个Chimp元素而未明确指定其名称空间,xpath将找不到新元素:

newChimp = mydom.createElement("Chimp")
compound.appendChild(newChimp)
print compound.toxml() # ok, two chimps now: <Compound><Chimp/><Chimp/></Compound>
print xpath.find("Chimp", compound) # wait a second, that's still only one chimp: [<DOM Element: Chimp at 0x24a0d88>]

重新解析修改后的XML后,xpath将找到两个元素:

mydom = minidom.parseString(mydom.toxml())
compound = xpath.findnode('/Zoo/Compound', mydom)
print xpath.find("Chimp", compound) # now it finds both chimps: [<DOM Element: Chimp at 0x24c9808>, <DOM Element: Chimp at 0x24c9888>]

此外,如果我使用命名空间创建新元素,xpath将在不重新解析的情况下找到它们:

babyChimp = mydom.createElementNS(mydom.firstChild.namespaceURI, "Chimp")
compound.appendChild(babyChimp)
print xpath.find("Chimp", compound) # that worked: [<DOM Element: Chimp at 0x24c9808>, <DOM Element: Chimp at 0x24c9888>, <DOM Element: Chimp at 0x24c9548>]

问题是:这种行为是正确还是错误? Chimp的名称空间不应该是隐含的吗?毕竟,无论我使用xml.dom.createElement()还是xml.dom.createElementNS(),生成的XML都是相同的。如果这是一个错误,那么它在哪里?在xml.domxpath

FWIW:我使用Python 2.7.5和2.7.4的Windows发行版观察到了这种行为,在这两种情况下我使用了xpath模块0.1。

1 个答案:

答案 0 :(得分:2)

简言之:

  

问题是:这种行为是正确还是错误?

行为似乎不正确或至少不需要,所以我会说这是错误或未完成的功能。

  

Chimp的名称空间不应该是隐含的吗?

在解析字符串时它是隐式的,但在创建并将该元素添加到树时它不是隐式的。

  

毕竟,无论我使用xml.dom.createElement()还是xml.dom.createElementNS(),生成的XML都是相同的。

生成的XML不一样,或者至少它不应该相同。

  

如果这是一个错误,那么它在哪里?

我猜测toxml()方法中存在一个错误,它不输出第二个Chimp元素的空默认命名空间,因此它继承了{{1}的默认命名空间重新解析修改后的文档时的元素。

更确切地说:

首先你的文件是

Zoo

然后,您尝试查找<Zoo xmlns='http://foo.bar/zoo'> <Compound><Chimp/></Compound> </Zoo> 元素,但不要为其定义任何名称空间上下文。但是,在这种情况下,Chimp模块似乎从节点的document元素初始化上下文,因此查找成功。

然后添加另一个xpath元素,但没有命名空间,您的文档变为

Chimp

第二次查找尝试只返回一个元素,因为另一个元素具有不同的名称空间。

我猜这个错误发生在这里,而不是上面的正确版本,<Zoo xmlns='http://foo.bar/zoo'> <Compound><Chimp/><Chimp xmlns=""/></Compound> </Zoo> 方法的输出实际上是

toxml()

重新解析该文本会导致第二个<Zoo xmlns='http://foo.bar/zoo'> <Compound><Chimp/><Chimp/></Compound> </Zoo> 元素继承与第一个相同的(默认)命名空间,因此第三个查找返回2个元素。

添加最后一个Chimp元素将导致文档变为

Chimp

完全相同
<Zoo xmlns='http://foo.bar/zoo'>
  <Compound><Chimp/><Chimp/><Chimp xmlns='http://foo.bar/zoo'/></Compound>
</Zoo>

同样,所有<Zoo xmlns='http://foo.bar/zoo'> <Compound><Chimp/><Chimp/><Chimp/></Compound> </Zoo> 元素都在同一名称空间中,因此find会将它们全部返回。

这个错误看起来很相似:http://bugs.python.org/issue1371937尽管它的状态,它实际上仍然是开放的,因为它被关闭作为另一个开放bug的副本。