我应该在c++中使用什么XML解析器?

我有需要解析的XML文档和/或需要构建XML文档并将它们写入文本(文件或内存)。既然c++标准库没有这样的库,我应该使用什么?

注意:这是一个明确的,c++ - faq风格的问题。所以,是的,它是其他东西的复制品。我没有简单地盗用那些问题,因为他们往往会问一些更具体的问题。这个问题比较一般。

199589 次浏览

就像标准库容器一样,应该使用什么库取决于您的需要。下面是一个简单的流程图:

enter image description here

第一个问题是:你需要什么?

我需要完全符合XML

你需要处理XML。不是玩具XML, 真正的 XML。你需要能够读取和写入XML规范的所有,而不仅仅是底层的,易于解析的位。您需要命名空间、DocTypes、实体替换等等。完整的W3C XML规范。

下一个问题是:您的API是否需要符合DOM或SAX?

我需要精确的DOM和/或SAX一致性

你需要的API是DOM和/或SAX。它不能只是sax风格的推送解析器,或者dom风格的保留解析器。它必须是实际的DOM或实际的SAX,在c++允许的范围内。

你选择了:

Xerces

这是你的选择。它几乎是唯一具有完全(或接近于c++所允许的)DOM和SAX一致性的c++ XML解析器/编写器。它还具有XInclude支持、XML Schema支持和大量其他特性。

它没有真正的依赖关系。它使用Apache许可证。

我不关心DOM和/或SAX的一致性

你选择了:

LibXML2

LibXML2提供了一个c风格的接口(如果您对此感到困扰,可以使用Xerces),尽管该接口至少在某种程度上是基于对象的,并且易于包装。它提供了很多特性,比如XInclude支持(带有回调,以便您可以告诉它从哪里获取文件)、XPath 1.0识别器、RelaxNG和Schematron支持(尽管错误消息留下了很多有待改进),等等。

它确实依赖于iconv,但它可以在没有依赖的情况下进行配置。尽管这确实意味着它可以解析的文本编码集将更加有限。

它使用MIT许可证。

我不需要完全符合XML

因此,完全符合XML并不重要。您的XML文档要么完全在您的控制之下,要么保证使用“基本子集”;XML:没有名称空间、实体等。

那么什么对你来说重要呢?下一个问题是:在XML工作中,对您来说最重要的是什么?

最大的XML解析性能

应用程序需要以尽可能快的速度将XML转换为c++数据结构。

你选择了:

RapidXML

这个XML解析器正如它的字面意思:快速XML。它甚至不处理将文件拉入内存;怎么做取决于你。它所处理的是将其解析为您可以访问的一系列c++数据结构。它这样做的速度和它逐个字节扫描文件的速度一样快。

当然,天下没有免费的午餐。与大多数不关心XML规范的XML解析器一样,Rapid XML不涉及名称空间、doctype、实体(字符实体和6个基本XML实体除外)等等。基本上就是节点,元素,属性等等。

此外,它还是一个dom风格的解析器。所以它要求你把所有的文本都读进去。然而,它不做的是复制任何文本(通常)。RapidXML获得大部分速度的方式是通过引用字符串就地。这需要更多的内存管理(在RapidXML查看该字符串时,必须保持该字符串是活的)。

RapidXML的DOM是最简单的。你可以得到字符串值。您可以通过名称搜索属性。差不多就是这样。没有将属性转换为其他值(数字、日期等)的方便函数。你只会得到字符串。

RapidXML的另一个缺点是它对写作 XML来说很痛苦。为了构建它的DOM,需要对字符串名称进行大量显式的内存分配。它确实提供了一种字符串缓冲区,但这仍然需要在您的端进行大量显式工作。它当然是功能性的,但使用起来很痛苦。

它使用MIT许可证。它是一个没有依赖关系的纯头文件库。

我关心业绩,但不是那么多

是的,性能对你很重要。但也许你需要一些不那么简单的东西。也许它可以处理更多的Unicode,或者不需要那么多用户控制的内存管理。性能仍然很重要,但你想要一些不那么直接的东西。

你选择了:

PugiXML

从历史上看,这是RapidXML的灵感来源。但是这两个项目出现了分歧,Pugi提供了更多的特性,而RapidXML则完全专注于速度。

PugiXML提供Unicode转换支持,因此如果您有一些UTF-16文档,并希望将它们读取为UTF-8, Pugi将提供。如果需要的话,它甚至有XPath 1.0实现。

但是Pugi仍然很快。与RapidXML一样,它没有依赖关系,并且在MIT许可证下分发。

阅读大型文档

你需要读取在大小上以字节度量的文档。也许你从stdin中获得它们,由其他进程提供。或者你正在从大量文件中读取它们。之类的。关键是,你需要的是必须将整个文件读入内存,以便处理它。

你选择了:

LibXML2

Xerces的sax风格API将在这种情况下工作,但这里有LibXML2,因为它更容易使用。sax风格的API是一个push-API:它开始解析流,然后触发您必须捕获的事件。您必须管理上下文、状态等等。读取sax风格API的代码比人们希望的要分散得多。

LibXML2的xmlReader对象是一个拉api。你去到下一个XML节点或元素;没人告诉你。这允许你根据需要存储上下文,以一种在代码中比一堆回调更可读的方式处理不同的实体。

选择

< a href = " https://libexpat.github。io noreferrer“rel = >外籍< / >

Expat是一个著名的c++解析器,它使用了一个拉式解析器API。作者是詹姆斯·克拉克。

它的当前状态是活动的。最新版本为2.2.9,于(2019-09-25)发布。

LlamaXML

它是一个stax风格API的实现。它是一个拉式解析器,类似于LibXML2的xmlReader解析器。

但自2005年以来就没有更新过。所以,买者自负。

XPath支持

XPath是一种在XML树中查询元素的系统。这是一种使用标准化语法有效地通过公共属性命名元素或元素集合的便捷方法。许多XML库提供XPath支持。

这里有三个有效的选择:

  • LibXML2:它提供完整的XPath 1.0支持。同样,它是一个C API,所以如果这让您感到困扰,还有其他选择。
  • PugiXML:它也支持XPath 1.0。如上所述,它更像是一个c++ API,而不是LibXML2,因此您可能更熟悉它。
  • TinyXML:它不附带XPath支持,但有TinyXPath库提供它。TinyXML正在向2.0版本进行转换,这将对API进行重大更改,因此TinyXPath可能无法与新API一起工作。和TinyXML本身一样,TinyXPath也是在zLib许可下发布的。

把工作做完

因此,您不关心XML的正确性。性能对你来说不是问题。流媒体无关紧要。你所需要的只是某物,它将XML放入内存,并允许你再次将其粘贴回磁盘。关心的是API。

您希望XML解析器体积小、易于安装、使用简单,并且小到与最终可执行文件的大小无关。

你选择了:

TinyXML

我之所以把TinyXML放在这个位置上,是因为它和XML解析器一样简单。是的,它很慢,但它简单而明显。它有很多方便的转换属性的函数。

在TinyXML中编写XML不是问题。你只需要new一些对象,将它们连接在一起,将文档发送到std::ostream,然后皆大欢喜。

还有一些围绕TinyXML构建的生态系统,具有更友好的迭代器API,甚至在其之上分层的XPath 1.0实现。

TinyXML使用zLib许可证,它或多或少是MIT许可证,只是名字不同。

还有另一种处理XML的方法可能需要考虑,称为XML 数据绑定。特别是如果您已经有了XML词汇表的正式规范,例如在XML Schema中。< / p >

XML数据绑定允许您使用XML,而无需实际执行任何XML解析或序列化。数据绑定编译器自动生成所有低级代码,并将解析后的数据表示为与应用程序域对应的c++类。然后通过调用函数和c++类型(int、double等)来处理这些数据,而不是比较字符串和解析文本(这是使用低级XML访问api(如DOM或SAX)所做的事情)。

看,例如,我写的一个开源XML数据绑定实现, CodeSynthesis XSD和,对于a 轻量级,无依赖版本,CodeSynthesis XSD / e < / > . < / p >

把我的也放上去。

http://www.codeproject.com/Articles/998388/XMLplusplus-version-The-Cplusplus-update-of-my-XML

没有XML验证特性,但是速度很快。

获得了全球, Inc.中,我们使用rapidxml。我们尝试了所有其他的,但rapidxml似乎是我们的最佳选择。

这里有一个例子:

 rapidxml::xml_document<char> doc;
doc.parse<0>(xmlData);
rapidxml::xml_node<char>* root = doc.first_node();


rapidxml::xml_node<char>* node_account = 0;
if (GetNodeByElementName(root, "Account", &node_account) == true)
{
rapidxml::xml_node<char>* node_default = 0;
if (GetNodeByElementName(node_account, "default", &node_default) == true)
{
swprintf(result, 100, L"%hs", node_default->value());
free(xmlData);
return true;
}
}
free(xmlData);

关于Expat的另一个注意事项:它值得用于嵌入式系统工作。然而,你可能在网上找到的文档是古老的和错误的。源代码实际上有相当完整的函数级注释,但需要仔细阅读才能理解。

那么,好吧。我已经创建了一个新的列表,因为没有一个不满足我的需求。

好处:

  1. 拉解析器流式API,即解析器就像迭代器没有回调或DOM树。例如读取XML到数据结构
  2. 异常和RTTI可以通过编译器选项关闭,错误处理可以通过std:: error_code完成
  3. 限制内存使用,支持大文件(用100mib XMark文件 from测试,速度取决于硬件)。有一个有限的COLLADA格式三维模型加载的例子
  4. UNICODE支持,并自动检测输入源编码

项目主页 .