Java核心技术·卷Ⅱ:高级特性(原书第10版)
上QQ阅读APP看书,第一时间看更新

3.6.1 使用SAX解析器

SAX解析器在解析XML输入数据的各个组成部分时会报告事件,但不会以任何方式存储文档,而是由事件处理器建立相应的数据结构。实际上,DOM解析器是在SAX解析器的基础上构建的,它在接收到解析器事件时构建DOM树。

在使用SAX解析器时,需要一个处理器来为各种解析器事件定义事件动作。ContentHandler接口定义了若干个在解析文档时解析器会调用的回调方法。下面是最重要的几个:

·startElement和endElement在每当遇到起始或终止标签时调用。

·characters在每当遇到字符数据时调用。

·startDocument和endDocument分别在文档开始和结束时各调用一次。

例如,在解析以下片段时:

解析器会产生以下回调:

1)startElement,元素名:font

2)startElement,元素名:name

3)characters,内容:Helvetica

4)endElement,元素名:name

5)startElement,元素名:size,属性:units="pt"

6)characters,内容:36

7)endElement,元素名:size

8)endElement,元素名:font

处理器必须覆盖这些方法,让它们执行在解析文件时我们想要让它们执行的动作。本节最后的程序会打印出一个HTML文件中的所有链接<a href="...">。它直接覆盖了处理器的startElement方法,以检查名字为a,且属性名为href的链接,其潜在用途包括用于实现“网络爬虫”,即一个沿着链接到达越来越多网页的程序。

注意:遗憾的是,HTML不必是合法的XML,大多数HTML页面都与良构的XML差别很大,以至于示例程序无法解析它们。但是,W3C编写的大部分页面都是用XHTML编写的,XHTML是一种HTML方言,且是良构的XML,你可以用这些页面来测试示例程序。例如,运行:

将看到那个页面上所有链接的URL列表。

示例程序是一个很好的使用SAX的例子。我们根本不在乎a元素出现的上下文环境,而且不必存储树形结构。

下面是如何得到SAX解析器的代码:

现在可以处理文档了:

这里的source可以是一个文件、一个URL字符串或者是一个输入流。handler属于DefaultHandler的一个子类,DefaultHandler类为以下四个接口定义了空的方法:

示例程序定义了一个处理器,它覆盖了ContentHandler接口的startElement方法,以观察带有href属性的a元素。

startElement方法有3个描述元素名的参数,其中qname参数以prefix:localname的形式报告限定名。如果命名空间处理特性已经打开,那么namespaceURI和lname参数提供的就是命名空间和本地(非限定)名。

与DOM解析器一样,命名空间处理特性默认是关闭的,可以调用工厂类的setNamespaceAware方法来激活命名空间处理特性:

在这个程序中,我们还处理了另一个常见的问题。XHTML文件总是以一个包含对DTD引用的标签开头,解析器会加载这个DTD。可以理解的是,W3C肯定不乐意对诸如www.w3.org/TR/xhtml/DTD/xhtml-strict.dtd这样的文件提供千万亿次的下载。总有一天他们会完全拒绝提供这些文件,但到写本章时为止,他们还在并不情愿地提供DTD下载。如果你不需要验证文件,只需调用:

程序清单3-8包含了网络爬虫程序的代码。在本章的后续部分,将会看到SAX的另一个有趣用法,即将非XML数据源转换成XML的一种简单方式是报告XML解析器将要报告的SAX事件。详情请参见3.8节。

程序清单3-8 sax/SAXTest.java

javax.xml.parsers.SAXParserFactory 1.4

·static SAXParserFactory newInstance()

返回SAXParserFactory类的一个实例。

·SAXParser newSAXParser()

返回SAXParser类的一个实例。

·boolean isNamespaceAware()

·void setNamespaceAware(boolean value)

获取和设置工厂的namespaceAware属性。当设为true时,该工厂生成的解析器是命名空间感知的。

·boolean isValidating()

·void setValidating(boolean value)

获取和设置工厂的validating属性。当设为true时,该工厂生成的解析器将要验证其输入。

javax.xml.parsers.SAXParser 1.4

·void parse(File f,DefaultHandler handler)

·void parse(String url,DefaultHandler handler)

·void parse(InputStream in,DefaultHandler handler)

解析来自给定文件、URL或输入流的XML文档,并把解析事件报告给指定的处理器。

org.xml.sax.ContentHandler 1.4

·void startDocument()

·void endDocument()

在文档的开头和结尾处被调用。

·void startElement(String uri,String lname,String qname,Attributes attr)

·void endElement(String uri,String lname,String qname)

在元素的开头和结尾处被调用。

参数:uri 命名空间的URI(如果解析器是命名空间感知的)

lname 不带前缀的本地名(如果解析器是命名空间感知的)

qname 元素名(如果解析器是命名空间感知的),或者是带有前缀的限定名(如果解析器除了报告本地名之外还报告限定名)

·void characters(char[]data,int start,int length)

解析器报告字符数据时被调用。

参数:data 字符数据数组

start 在作为被报告的字符数据的一部分的字符数组中,第一个字符的索引

length 被报告的字符串的长度

org.xml.sax.Attributes 1.4

·int getLength()

返回存储在该属性集合中的属性数量。

·String getLocalName(int index)

返回给定索引的属性的本地名(无前缀),或在解析器不是命名空间感知的情况下返回空字符串。

·String getURI(int index)

返回给定索引的属性的命名空间URI,或者,当该节点不是命名空间的一部分,或解析器并非命名空间感知时返回空字符串。

·String getQName(int index)

返回给定索引的属性的限定名(带前缀),或当解析器不报告限定名时返回空字符串。

·String getValue(int index)

·String getValue(String qname)

·String getValue(String uri,String lname)

根据给定索引、限定名或命名空间URI+本地名来返回属性值;当该值不存在时,返回null。