本技巧文章中说明了怎样使用部分验证方法,而不使用解析 API 所包括的完全验证。通过只验证绝对要求验证的那部分,可以节省大量处理时间。
基于 SAX 的编程的基础之一就是验证。在 XML 术语中,验证的意思就是确认一个 XML 文档是符合 DTD 或 XML Schema 的。
传统验证
当然,传统的验证方式有其用途。如果您从不可靠的来源(例如因特网)处接收 XML 文档或者允许用户或开发人员手工编辑 XML 文档,那么,验证可能是个好办法,通过验证可以确保这些文档没有异常情况而且确保您的应用程序免于处理无效 XML 的麻烦。您可以通过 SAX 中的
setFeature() 方法来启用验证,如
清单 1所示。
XMLReader reader = XMLReaderFactory.createXMLReader();
reader.setFeature("http://xml.org/sax/features/validation",
true);
reader.parse(myInputSource);
这种验证的问题是它的过程极其繁复。验证每个元素(及其内容)、每个属性、已解析的实体引用内容以及其它东西会花费大量时间。每次进行 XML 文档解析,都会增加您的应用程序的开销。
定制验证
较佳的解决方案是使用某种
定制的验证。在这种方法中,您把业务规则指定到您的验证中。为了更好地理解这一点,请考虑
清单 2中所示的 DTD 片段。
<!ELEMENT purchaseOrder (item+, billTo, shipTo, payment)>
<!ATTLIST purchaseOrder
id CDATA #REQUIRED
tellerID CDATA #REQUIRED
orderDate CDATA #REQUIRED
>
采用传统验证时,当处理一个
purchaseOrder 元素时,解析器必须确保它至少有一个
item 子元素,以及
billTo 、
shipTo 和
payment 子元素。它还要确保具有
id 、
tellerID 和
orderDate 属性。这表面上听起来很好:所有这些都是必需的,因此,可以确定这些子元素都存在是没有问题的。然而,事实上所有数据都被用在同一个业务组件的情况是很少有的。在一个应用程序组件中,您可能需要知道输入订单的出纳员的标识以及订单的输入日期;这在雇员事务的审计中很普遍。在另一个应用程序组件(如订单执行)中,您可能需要子元素,但不需要任何属性。
在这两个情况中,实际只需要对输入的 XML 进行部分验证,而且只需检查整个约束集的一部分。如果您自己处理这部分验证,而且关闭了解析器的
全局 验证,那么您的应用程序的性能就会得到明显的改进。譬如,如果您需要确保
id 和
tellerID 属性都存在,那么您可以关闭这个全局验证,如下面的
清单 3所示。
reader.setFeature("http://xml.org/sax/features/validation", false);
您可以实现如
清单 4 所示的 SAX
startElement() 回调中的逻辑,它会处理任何所需的定制的部分验证。
public startElement(String namespaceURI, String localName,
String qName, Attributes attributes)
throws SAXException {
// Handle custom validation
if (localName.equals("purchaseOrder")) {
if (attributes.getIndex("tellerID") < 0) {
throw new SAXException("Error: purchaseOrder elements must contain " +
"a tellerID attribute.");
}
if (attributes.getIndex("orderDate") < 0) {
throw new SAXException("Error: purchaseOrder elements must contain " +
"an orderDate attribute.");
}
}
// Normal XML processing
}
这看起来似乎是极其简单的概念,事实上,它就是这么简单!但是,通过执行这段代码而不是进行完全验证,您会看到您的应用程序会在性能上得到极大的改进。您会发现基于 SAX 的应用程序运行得比以往任何时候都更快和更顺畅。