建快捷的XML解析器
——使用最少的代码解析有效的XML
XML 是一种非常受欢迎的数据格式,它可以由人工阅读,并且具有自描述功能,便于使用。但不幸的是,许多基于 Java 的 XML
解析器都很庞大。例如 Sun Microsystem 的 jaxp.jar 和 parser.jar 库各有 1.4 MB。如果你在内存有限(如在 J2ME
环境中),或者受带宽限制(如用于 Applet),使用那些巨大的解析器就是不可行的。
那些库文件之所以如此巨大,部分原因是它们拥有非常多的功能。这些功能也许远比你所需要的多,它们包括验证 XML DTD
(文档类型定义)、可能的计划等。然而,你可能已经知道你的应用程序所接收的就是有效的 XML。你也可能已经决定使用 UTF-8
字符集。因此,你真正需要基于事件驱动的方式来处理 XML 元素和 XML 实体的转化——一个不需要校验正确性的解析器。
为什么不直接使用 SAX?
你可以有限制的的实现 SAX (Simple
API for XML, XML 的简单 API) 接口的功能,在遇到某些不需要的东西时抛出一个名为 NotImplemented 的异常。
的确,你能开发一些非常小的东西,让它们远小于有 1.4 MB 大小的 jaxp.jar/parser.jar
库。甚至你能定义自己的类来减小代码的长度。实际上,我们构造的包将远小于包含 SAX 接口定义的 jar 文件。
我们快捷的解析器像 SAX
解析器同样是以事件驱动为基础的。同 SAX 解析器一样,它让你实现一个接口以捕捉和处理与属性和开始/结束元素标记相关的事件。如果你用过 SAX
解析器,那么使用该解析器的时候就会轻车熟路。
限制 XML 功能
许多人喜欢简单、具有自描述功能的文本型数据格式
XML。他们希望能容易的选出元素,属性和值,以及元素的文本内容。在此基础上,让我们来想一想需要保留些什么功能。
我们的解析包简单得只包含一个类,QDParser,和一个接口,DocHandler。QDParser 类有一个公共静态方法
parse(DocHandler, Reader),我们可以像实现一个有限态机器一样实现它。
我们限制了解析器处理 DTD
<!DOCTYPE> 的功能,把 <?xml version="1.0"?>
标记当作注释来处理。因此这个解析器既不会觉得处理这些东西麻烦,也不会使用它们的内容。
由于我们不会处理
DOCTYPE,所以我们的解析器不能读入定制的实体定义。我们只会用到标准的实体:&, <, >, ', 和
"。如果这是个问题,你可以插入一段代码来展开定制的实体,就像源码中展示的那样。你可以把预处理文档作为选择之一——将定制的实体定义替换成为它们展开后的文本,这个预处理过程应该在把文档交给
QDParser 之前进行。
我们的解析器也不支持有条件的部分。例如,<![INCLUDE[...]]> 或者
<![IGNORE[ ... ]]>。既然没有在 DOCTYPE
中定制实体定义的能力,就不会需要这个功能。就算需要这样的功能,我们也可以在把数据送到空间受限的应用程序之前进行这部分处理。
由于我们不会处理属性的定义,XML 规范让我们把所有的属性都认为是 CDATA 类型。这样,我们能很容易的使用
java.util.Hashtable 代替 org.xml.sax.AttributeList 来保存元素的属性列表。我们只需要Hashtable
的名/值对信息,不需要 getType() 方法,因为它总是返回 CDATA。
由于不能处理属性定义,也导致了一些问题。例如,解析器不支持默认属性值;另外,它也不能自动去掉用在 NMTOKENS
定义中的空格符。然而,我们可以在准备 XML 文档的时候处理掉这两个问题,所以在应用程序中使用这个解析器的时候可以不使用其它程序。
实际上,所有被去掉的功能都可以在准备文档的时候得到适当的补偿。你可以在准备文档的那一步填补快捷解析器所不具备的特性(如果你需要这些特性的话)
解析器功能
这个解析器所不能做的就这些了。那么他能做什么呢?
它能辩认所有元素的开始标记和结束标记
它能列出属性列表,这些属性的值是写在单引号或者双引号中
它能辩认出
<[CDATA[...]]> 结构
它能辩认出标准实体:&, <, >, ", 和
&apos,也能辩认出数字实体
在输入过程中它将行结束标记 \r\n 和 \r 映射为 \n,和 XML 规范一致,2.11 部分
这个解析器只进行最基本的错误检查。如果遇到不正确的语法,比如未知的实体,它会抛出异常。无论如何,这个解析器不作有效性检查,它假定接收到的 XML
文档就是正确的。
如何使用这个包
使用快捷 XML 解析器非常容易。首先,实现
DocHandler 接口。然后就可以毫不费劲的解析一个名为 config.xml 的文件:
DocHandler doc = new
MyDocHandler();
QDParser.parse(doc,new FileReader("config.xml"));
源码中包含了两个提供完整 DocHandler 实现的例子。第一个 DocHandler 名为 Reporter,简单地报告所有事件到
System.out,就像在读它们一样。你可以用样例文件 (config.xml) 来测试 Reporter。
第二个是更复杂的例子,Conf。它能更新内存中已经存在的数据结构的域。Conf 使用 java.lang.reflect 包以定位存在于
config.xml
中的域和对象。如果运行这个程序,它会输出诊断信息来告诉你它更新了什么对象和如何更新这些对象的。如果配置文件要求它更新不存在的域,它会输出错误信息。
修改这个包
你也许想修改这个包以适应你的应用程序。可以添加定制的实体定义——QDParser.java 文件的第 180
行包含了“在这里添加定制实体定义”的注释。
你也能添加有限太机器的功能,恢复我在这里去掉的一些功能。由于代码量很小,做这件事情也非常容易。
占用很少的空间
QDParser 类在你编译并打包为 jar 文件之后只占用 3KB
的磁盘空间。它的源码,包括注释,也才刚刚 300 行多一点。这在保留了 XML 规范中足够多的有用特性的同时,还使它可以用于空间受限的应用程序中。
如果您希望与本文章的作者或其所在机构,进一步交流,请联系:畅享网 姜小姐
jill.jiang@amt.com.cn | 021-51096826-112 |
在线联系