内容


利用 Xerces-C++ 提高 XML 应用程序的性能

探究 Xerces-C++ 的属性和功能、数据处理和模式语法缓存

Comments

随着 Web 服务和面向服务架构(SOA)的出现,XML 已变得非常流行。它对于应用程序之间和 Web 上的数据交换起着非常重要的作用,并且是许多性能关键型场景的基础。

您可以通过有效地使用解析器提高 XML 应用程序的性能。Xerces-C++ 是可从 Apache 获得的开放源码 XML 验证解析器。本文将展示许多利用 Xerces-C++ 解析器提高应用程序性能的技巧。

Xerces-C++ 是一个作为共享库提供的 XML 验证解析器。这个库包含针对 DOM 和 SAX 的接口。具体地说,SAXParser 是针对 SAX 1.0 规范的接口:SAX2XMLReader 是针对 SAX 2.0 规范的接口;XercesDOMParser 是针对 DOM 规范的接口;DOMBuilder 是 DOM Level 3.0 Abstract Schemas and Load and Save 规范的 Load 接口的一个实现。

属性和特性

许多属性和特性对解析器的性能都有很大的影响。

使用正确的扫描程序

Xerces-C++ 的主要组件之一是扫描程序。它不只负责扫描 XML 实例,而且在评定 XML 文档的有效性方面起着重要作用。

Xerces-C++ 有四个扫描程序:IGXMLScanner、WFXMLScanner、DGXMLScanner 和 SGXMLScanner。为您的场景选择合适的扫描程序对于获得更好的性能非常重要。IGXMLScanner(默认的扫描程序)是一个通用的扫描程序,它不仅处理格式是否良好,而且针对 DTD 和/或 XML Schema 验证 XML 文档。另一方面,WFXMLScanner 只负责检查格式是否良好而不进行语法验证。如果您只关注文档的格式良好性就使用 WFXMLScanner。如果只进行 DTD 验证就使用 DGXMLScanner,如果只进行 XML Schema 验证就使用 SGXMLScanner。

通过在 SAX2XMLReader API 或 DOMBuilder API 中设置扫描程序属性,可以通知解析器使用哪个扫描程序。清单 1 说明如何在 SAX2XMLReader 上设置扫描程序。

清单 1. 在 SAX2XMLReader API 上设置扫描程序
#include <xercesc/internal/XMLGrammarPoolImpl.hpp>
#include <xercesc/sax2/XMLReaderFactory.hpp>
#include <xercesc/util/XMLUni.hpp>

XMLGrammarPool *grammarPool = new XMLGrammarPoolImpl(XMLPlatformUtils::fgMemoryManager);
SAX2XMLReader* parser = XMLReaderFactory::createXMLReader(
                               XMLPlatformUtils::fgMemoryManager, grammarPool);

parser->setProperty(XMLUni::fgXercesScannerName, (void *)XMLUni::fgSGXMLScanner);

对 SAXParser API 或 XercesDOMParser API,您可以调用 useScanner 方法来指定解析器应该使用哪个扫描程序,如 清单 2 所示。

清单 2. 在 SAXParser API 上设置扫描程序
#include <xercesc/parsers/SAXParser.hpp>
#include <xercesc/util/XMLUni.hpp>
				
SAXParser parser = new SAXParser();
parser->useScanner(XMLUni::fgDGXMLScanner);

要了解更多关于如何在 Xerces-C++ 中使用特定扫描程序的信息,请参阅 参考资料

控制验证

指定了使用哪种扫描程序后,您仍然可以使用一些特性来控制解析器是否执行验证。

通过在 SAX2XMLReader API 或 DOMBuilder API 中设置验证特性,可以告诉解析器如何验证一个实例文档。清单 3 说明了如何实现上述功能。

清单 3. 在 DOMBuilder API 中设置验证
#include <xercesc/dom/DOMImplementationLS.hpp>
#include <xercesc/dom/DOMImplementationRegistry.hpp>
#include <xercesc/dom/DOMBuilder.hpp>
#include <xercesc/util/XMLUni.hpp>
				
static const XMLCh gLS[] = { chLatin_L, chLatin_S, chNull };
DOMImplementation *impl = DOMImplementationRegistry::getDOMImplementation(gLS);
DOMBuilder *parser = ((DOMImplementationLS*)impl)->createDOMBuilder(
                       DOMImplementationLS::MODE_SYNCHRONOUS, 0);

/* specify a validating parse */
parser->setFeature(XMLUni::fgDOMValidateIfSchema, true);
parser->setFeature(XMLUni::fgDOMValidation, true);

/* for SAX2 use XMLUni::fgSAX2CoreValidation and XMLUni::fgXercesDynamic */

对 SAXParser API 或 XercesDOMParser API,您可以调用 setValidationScheme 方法来指定验证,如 清单 4 所示。

清单 4. 在 XercesDOMParser API 中设置验证
#include <xercesc/parsers/XercesDOMParser.hpp>

XercesDOMParser *parser = new XercesDOMParser();
/* specify a non-validating parse */
parser->setValidationScheme(XercesDOMParser::Val_Never);

其他特性

表 1 展示了一些影响解析器性能的其他特性。

表 1. 其他 Xerces-C++ 性能特性
SAX2/DOM Level 3 setFeature XMLUni 成员说明
SAX1/Xerces DOM 解析器设置方法
fgXercesLoadExternalDTD控制是否解析外部的 DTD
setLoadExternalDTD
fgXercesCalculateSrcOfs控制对源代码偏移量的计算,此特性开销较大
setCalculateSrcOfs
fgXercesIdentityConstraintChecking控制是否检查模式一致性约束
setIdentityConstraintChecking
fgXercesIgnoreAnnotations控制在遍历模式时是否忽略注释
setIgnoreAnnotations
fgXercesSchemaFullChecking控制是否全面检查模式中的额外错误(查找这些错误非常耗时或消耗内存)
setValidationSchemaFullChecking

Xerces-C++ 编程指南(参阅 参考资料)描述了 Xerces-C++ 的特性。

数据处理

一些 API 对解析器的性能有重大影响,因为对具有单一实例的文档进行解析可能会大量调用这些函数。

避免对 XMLString::transcode() 进行不必要的调用

如果您提前知道要进行编码转换的字符串的内容,最好为该字符串创建一个 XMLCh 字符串常量,如 清单 5 所示,而不是调用 XMLString::transcode()

清单 5. 定义 XMLCh 字符串
// define a constant XMLCh string for 'Element_Name'
#include <xercesc/util/XMLUniDefs.hpp>

XMLCh Element_Name[] = {  chLatin_E, chLatin_l, chLatin_e, chLatin_m, chLatin_e, 
                          chLatin_n, chLatin_t, chUnderscore, chLatin_N, chLatin_a, 
                          chLatin_m, chLatin_e, chNull };

通过使用常量字符串,可以避免分配内存、复制字符串和编码转换过程。您也不必释放由 transcode 函数返回的内存,因为此时这是调用者的责任。频繁地在 XMLChchar 之间进行编码转换会严重影响性能,因此在可能的情况下尽量使用一种格式处理数据。

获得 XMLCh 字符串常量的一个简单方法是使用脚本目录中的 makeStringDefinition.pl。Xerces-C++ 使用许多在头文件 xercesc/util/XMLUni.hpp 中定义的预定义符号。

避免调用 XMLString::stringLen() 来检查零长度的字符串

如果您只是想要检查一个零长度字符串,而不是调用 XMLString::stringLen(string),只需要检查该字符串是不是 NULL 或者它的第一个字符是不是空字符。清单 6 说明如何避免使用 XMLString::stringLen() 来检查一个零长度的字符串。

清单 6. 检查零长度字符串
if (xmlStr == 0 || *xmlStr == 0) {
// string is zero length
}

这段代码帮助您避免额外地传递字符串。

避免调用 XMLString::compareIString()

XMLString::compareIString() 方法使用编码转换程序来进行不区分大小写的字符串比较。如果已知所要比较的数据只有字母字符(A 到 Z),您应该使用 XMLString::compareIStringASCII() 例程。这个例程检查字符是否在 A 到 Z 之间,然后将其转换为小写字母并将它直接和其他字符比较。

使用 XMLString::compareIStringASCII() 避免了调用编码转换服务执行比较。作为一种替代方法,您可以调用 XMLString::equals() 方法。清单 7 展示了三种方法。

清单 7. 字符串比较
#include <xercesc/util/XMLString.hpp>

XMLCh*  data;
XMLCh Element[] = {  chLatin_E, chLatin_l, chLatin_e, chLatin_m, chLatin_e, 
                     chLatin_n, chLatin_t, chNull }; 

if (XMLString::compareIString(data, Element) == 0) { ... }
/* Since Element only has the characters A to Z, this could be done more 
   efficiently using: */
if (XMLString::compareIStringASCII(data, Element) == 0) { ... }

/* Even better is when you don't require a case-sensitive comparison */

if (XMLString::equals(data, Element)) { ... }

最小化处理程序

如果您只想测试一个文档是否格式良好和(或)是否是有效的,只要注册一个 ErrorHandler。当注册一个 DocumentHandler 和/或 AdvDocHandler 时,将执行从 Xerces-C++ 库到应用程序的额外的不必要的调用,如果使用 AdvDocHandler 只为了得到 XMLDecl 回调信息,您就可以在获得 XMLDecl 信息后调用 removeAdvDocHandler

在序列化过程中避免使用 XMLFormatter::UnRep_Fail

如果您试图格式化 XML,请谨慎地使用 XMLFormatter::UnRep_Fail。这个选项将检查每个字符,查看它是否可以被转换成目标编码。如果原始文档的编码和目标编码相同,并且目标编码中的修改仍然有效,您应该使用 XMLFormatter::UnRep_CharRef

模式语法缓存

如果进行模式验证和重用同一模式,需要使用 Xerces-C++ 的语法缓存特性。更多信息请查看 developerWorks 文章 “使用 Xerces-C++ 缓存和序列化 XML 模式”(请参阅 参考资料)。

Xerces-C++ 初始化

根据 Xerces-C++ 线程模型,主线程调用 XMLPlatformUtils::Initialize()。然后您可以创建其他用于解析的线程(每个线程创建一个解析器)。最后主线程调用 XMLPlatformUtils::Terminate()

XMLPlatformUtils::Initialize() 是一个开销较大的操作。即使不使用多线程处理,也要预先初始化 Xerces-C++ 以避免多次调用这个函数,然后在应用程序结束时将其终止。如 清单 8 所示,将 XMLPlatformUtils::Initialize 的最后一个参数 toInitStatics 指定为 true,将使初始化过程变得更长,但它能产生更好的解析性能,因为它预先初始化了所有静态变量。

清单 8. 通过初始化调用实现所有静态变量的初始化
#include <xercesc/util/PlatformUtils.hpp>
				
XMLPlatformUtils::Initalize(XMLUni::fgXercescDefaultLocale, 0, 0, 0, true);

类似地,在初始化期间可以创建一个解析器池(每个线程创建一个)。当需要解析文档时,可以在运行时使用这些解析器,从而避免构造和分解解析器的开销。

结束语

在本文中,我们展示了许多关于如何使用 Xerces-C++ XML 解析器提高应用程序性能的技巧和建议。 采用这些建议能够降低解析 XML 文档时 Xerces-C++ 的 CPU 消耗。


相关主题


评论

添加或订阅评论,请先登录注册

static.content.url=http://www.ibm.com/developerworks/js/artrating/
SITE_ID=10
Zone=XML, SOA and web services
ArticleID=315070
ArticleTitle=利用 Xerces-C++ 提高 XML 应用程序的性能
publish-date=06192008