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

日期: 2008-06-24 作者:David Cargill 来源:TechTarget中国

  XML已成为应用程序之间和在Web上进行数据交换的主要组成。通过本文学习如何适当地使用Xerces-C++来提高XML应用程序的性能。您将了解到有效使用解析器的最佳方法以及哪些特性和属性将影响解析器的性能。


  随着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函数返回的内存,因为此时这是调用者的责任。频繁地在XMLCh和char之间进行编码转换会严重影响性能,因此在可能的情况下尽量使用一种格式处理数据。


  获得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消耗。


  关于作者
  
  David Cargill是加拿大IBM XML Parser Development团队的成员之一。过去五年中他参与了Xerces-C++解析器的开发工作。

我们一直都在努力坚持原创.......请不要一声不吭,就悄悄拿走。

我原创,你原创,我们的内容世界才会更加精彩!

【所有原创内容版权均属TechTarget,欢迎大家转发分享。但未经授权,严禁任何媒体(平面媒体、网络媒体、自媒体等)以及微信公众号复制、转载、摘编或以其他方式进行使用。】

微信公众号

TechTarget微信公众号二维码

TechTarget

官方微博

TechTarget中国官方微博二维码

TechTarget中国

相关推荐

  • SAP收购CallidusCloud 与Salesforce竞争

    一直被称为后台办公巨头的SAP现在似乎也想在前台办公大展拳脚。 最新的迹象是SAP收购CallidusClou […]

  • 事件驱动框架和SOA在空军的应用

    空军正在利用SOA来改善数据共享,并实时跟踪战机,美国空军机动司令部的Michael Marek解释了企业可从中学习的经验。

  • 揭秘New Relic APM技术细节

    New Relic应性能管理(APM)套件主要用于Web软件开发。它允许用户在面向服务的架构(SOA)上跟踪关键事务性能,并且支持代码级别的可见性来评估特定代码段和SQL语句对性能的影响

  • 仅凭SOA和云无法解决业务数据管理风险问题

    SOA和云可以是某些恼人问题高效的解决方案;这一点我们已经知道了。但是也要记住它们并不是所有事情的直接答案,特别是当你的问题是业务数据管理风险,而不是技术问题时。