DOM是什么?DOM的全称是Document Object Model,即文档对象模型。
可能前端同学对这个会比较熟悉;事实上,HTML中的DOM和XML中的DOM是同一个概念。我们来看看W3C对DOM的定义:
“The DOM” is an API for accessing and manipulating documents (in particular, HTML and XML documents)
也就是说,DOM是一个对文档(尤其是指的是HTML和XML中的文档)进行访问和修改的API。
所以,顾名思义,DOM parser就是一个对DOM进行解析的工具,把XML加载到内存中,解析成树型结构,也就是所谓的DOM树。并且,树的每一个结点都是对象,封装了数据,并且提供了相应的操作方法。
为什么是树,而不是别的结构?因为,文档具有层次结构的特点,属于那种类型相同(都是element),并且层数可变的数据。对于这种数据,使用树能够有效降低深度,提供比较高的检索效率。当然,这种结构也有一个弱点,就是因为类型相同,约束实际上很弱,只需要满足很少的一些条件就可以被称为是“格式良好”的;因此,我们引入了词汇表,包括DTD和Schema,来对语义做进一步的校验,以确保XML是“合法”的。
格式良好其实是客观需求,至少在当时是。因为,小型设备往往缺乏解释“糟糕”的标记语言的计算资源/计算能力,所以需要我们满足一些规范,以减少解析的开销。
格式良好的XML需要满足以下几个条件,也就是所谓的“5 + 1规则”。其中,5条定义语法结构,1条保证一致性:
单根元素
元素标签规则
元素嵌套规则
元素规则
命名
内容
XML文档由使用标签对表示的元素、可选的属性和可选的在开始标签和结束标签之间的数据构成
混合内容:数据 + 其他内容
PCDATA(被解析的字符数据):提取并检查
CDATA(字符数据):使用<![CDATA[...]]>进行包装,不处理
HTML、XML、JS代码、需要大量转义的文本,等等
内部的文本遵循源语言的转义规则
类似于其他语言中的raw string,我没有暗示Python的意思。
要不然叫template string吧。
元素属性
XML声明(一致性)
DOM的解析过程分为下面几步:
理想情况下,DOM应该是很简单的。但是,实际使用时,为了可读性(没有谁会愿意读那种全部放在一行的XML吧),会插入很多换行符和空白符,这些换行符和空白符也会以text结点的形式出现在DOM树中。同时,结点还会拥有属性,这就让整个DOM的模型复杂了很多。
不过,归根结底,它们都是结点,都拥有共同的操作方法,这给我们的处理过程带来了便利。
在DOM中,所有的内容都是结点,包含这些属性:
包含一些通用的方法:
有四种主要的结点类型:
以及一些不那么常见的结点类型:
比较特殊的是,DOM会有一个document结点,包含全部的DOM元素。至于为什么,刚才已经提到过了,格式良好的文档应该满足单根元素规则,也就是只有一个根结点,并且是树结构而不是森林结构。可以想象,如果没有document结点,因为还会有别的元素出现,整个DOM就会变成森林结构。
从某种程度上来说,document类似于所谓的“哑结点”。
刚才提到,为了可读性,我们会往XML里插入很多换行符和空白符,这些换行符和空白符也会以text结点的形式出现在DOM树中。这个有时候会带来一些让人迷惑的现象,比如:
<book ibsn="123-765">
<author>Tom</author>
<title>Foo</title>
<price>$6</price>
</book>
请问book有几个子节点?答案是7个,3个元素,4个空白符形成的text结点。
比较神秘的是,属性不“属于”DOM结构的内容。因为,属性结点没有父结点,也没有子结点。在HTML中,属性是以NamedNodeMap的形式出现在父结点的属性中的。
一个DOM应用,一般来说,会有四个步骤:
这些就是一些纯粹的实现细节上的问题,就不再赘述了。
值得一提的是,在处理DOM parser的异常时,可能会有两种处理方式,一种是原生的try-catch,另一种是SAX风格的错误处理,通过使用setErrorHandler
方法来指定错误处理的逻辑。如果大家还有印象的话,这个其实就是命令模式。它还有一个更通俗的叫法,“回调函数”。