5.3. XML 分析

正如我说的,实际对一个XML文档的分析非常简单:一行代码。从这里出发到哪儿去就是你的事了。

例 5.7. 将入一个XML文档(这次是真的)

>>> from xml.dom import minidom                                          1
>>> xmldoc = minidom.parse('/a/diveintopython/common/py/kgp/binary.xml') 2
>>> xmldoc                                                               3
<xml.dom.minidom.Document instance at 010BE87C>
>>> print xmldoc.toxml()                                                 4
<?xml version="1.0" ?>
<grammar>
<ref id="bit">
  <p>0</p>
  <p>1</p>
</ref>
<ref id="byte">
  <p><xref id="bit"/><xref id="bit"/><xref id="bit"/><xref id="bit"/>\
<xref id="bit"/><xref id="bit"/><xref id="bit"/><xref id="bit"/></p>
</ref>
</grammar>
1

正如我们在前一节中所看到的,这样会从 xml.dom 包中导入 minidom 模块。

2

这儿就是进行所有工作的一行代码:minidom.parse 接收一个参数,并返回一个分析过的XML文档的表示。这个参数可以是许多东西;在本例中,它只是一个在我的硬盘上的XML文档文件名。(为了照着执行,你需要将路径改为指向下载的例子所在的目录。)但你也可以传入一个文件对象,或甚至是一个类似文件的对象。我们将在本章后面好好地利用这一灵活性。

3

minidom.parse 中返回的对象是一个 Document 对象,一个 Node 类的子类。这个 Document 对象是一个复杂的树状的联锁Python对象结构的根层次。这些Python对象完全表示了我们传给 minidom.parse 的XML文档。

4

toxmlNode 类的一个方法(因此可以在我们从 minidom.parse 中得到的 Document 对象上使用)。toxml 会打印出这个 Node 所表示的XML。对于 Document 结点,这样会打印出整个XML文件。

既然我们在内存中有了一个XML文件,就可以对其进行遍历了。

例 5.8. 得到子结点

>>> xmldoc.childNodes    1
[<DOM Element: grammar at 17538908>]
>>> xmldoc.childNodes[0] 2
<DOM Element: grammar at 17538908>
>>> xmldoc.firstChild    3
<DOM Element: grammar at 17538908>
1

每个 Node 都有一个 childNodes 属性,它是 Node 对象的一个列表。一个 Document 只有一个子结点,即XML文档的根元素(在本例中,是 grammar 元素)。

2

为了得到第一个(在本例中,只有一个)子结点,只要使用正常的列表语法。回想一下,这里没发生什么特别的,这儿只是一个正常Python对象的正常Python列表。

3 Since getting the first child node of a node is a useful and common activity, the Node class has a firstChild attribute, which is synonymous with childNodes[0]. (There is also a lastChild attribute, which is synonymous with childNodes[-1].)

例 5.9. toxml 可用于任何结点

>>> grammarNode = xmldoc.firstChild
>>> print grammarNode.toxml() 1
<grammar>
<ref id="bit">
  <p>0</p>
  <p>1</p>
</ref>
<ref id="byte">
  <p><xref id="bit"/><xref id="bit"/><xref id="bit"/><xref id="bit"/>\
<xref id="bit"/><xref id="bit"/><xref id="bit"/><xref id="bit"/></p>
</ref>
</grammar>
1

因为 toxml 定义在 Node 类中,它在任何XML结点中都可用,不只是 Document 元素。

例 5.10. 子结点可能是文本

>>> grammarNode.childNodes                  1
[<DOM Text node "\n">, <DOM Element: ref at 17533332>, \
<DOM Text node "\n">, <DOM Element: ref at 17549660>, <DOM Text node "\n">]
>>> print grammarNode.firstChild.toxml()    2



>>> print grammarNode.childNodes[1].toxml() 3
<ref id="bit">
  <p>0</p>
  <p>1</p>
</ref>
>>> print grammarNode.childNodes[3].toxml() 4
<ref id="byte">
  <p><xref id="bit"/><xref id="bit"/><xref id="bit"/><xref id="bit"/>\
<xref id="bit"/><xref id="bit"/><xref id="bit"/><xref id="bit"/></p>
</ref>
>>> print grammarNode.lastChild.toxml()     5


1

查看 binary.xml 中的XML,你可能会认为 grammar 只有两个子结点,两个 ref 元素。但你忘了什么了:硬回车符!在 '<grammar>' 之后,第一个 '<ref>' 之前,是一个硬回车符,并且这个文本算作 grammar 元素的一个子结点。同样,在每个 '</ref>' 之后都有一个硬回车符,这些也都作成子结点。所以 grammar.childNodes 实际上是一个有 5 元素的列表:3 个 Text 对象和 2 个 Element 对象。

2 第一个子结点是一个 Text 对象,它表示在 '<grammar>' 标记之后,在第一个 '<ref>' 之前的硬回车符。
3 第二个子结点是一个表示第一个 ref 元素的 Element 对象。
4 第四个子结点是一个表示第二个 ref 元素的 Element 对象。
5 最后一个子结点是一个 Text 对象,它表示在 '</ref>' 结束标记之后,在 '</grammar>' 之前的硬回车符。