提问人:Nicol Bolas 提问时间:2/22/2012 最后编辑:Peter O.Nicol Bolas 更新时间:10/17/2023 访问量:206719
我应该在 C++ 中使用什么 XML 解析器?[关闭]
What XML parser should I use in C++? [closed]
问:
我们不允许提出有关书籍、工具、软件库等建议的问题。您可以编辑问题,以便用事实和引文来回答。
5年前关闭。
我有需要解析的 XML 文档和/或需要构建 XML 文档并将它们写入文本(文件或内存)。由于 C++ 标准库没有用于此的库,我应该使用什么?
注意:这是一个明确的、C++-FAQ风格的问题。所以是的,它是其他人的复制品。我没有简单地挪用这些其他问题,因为它们倾向于要求一些更具体的东西。这个问题比较笼统。
答:
就像使用标准库容器一样,应该使用哪个库取决于您的需求。下面是一个方便的流程图:
所以第一个问题是:你需要什么?
我需要完全符合 XML 的要求
好的,所以你需要处理 XML。不是玩具XML,真正的XML。您需要能够读取和写入所有 XML 规范,而不仅仅是低级、易于解析的位。你需要命名空间、文档类型、实体替换等。完整的 W3C XML 规范。
下一个问题是:你的 API 需要符合 DOM 还是 SAX?
我需要精确的 DOM 和/或 SAX 一致性
好的,所以你真的需要 API 是 DOM 和/或 SAX。它不能只是 SAX 样式的推送解析器,或者 DOM 样式的保留解析器。在 C++ 允许的范围内,它必须是实际的 DOM 或实际的 SAX。
您已选择:
这是你的选择。它几乎是唯一一个完全(或接近 C++ 允许)DOM 和 SAX 一致性的 C++ XML 解析器/编写器。它还具有 XInclude 支持、XML 模式支持和大量其他功能。
它没有真正的依赖关系。它使用 Apache 许可证。
我不关心 DOM 和/或 SAX 一致性
您已选择:
LibXML2 提供了一个 C 风格的接口(如果这真的困扰你,那就去使用 Xerces),尽管该接口至少在某种程度上是基于对象的并且很容易包装。它提供了许多功能,例如 XInclude 支持(带有回调,以便您可以告诉它从哪里获取文件)、XPath 1.0 识别器、RelaxNG 和 Schematron 支持(尽管错误消息还有很多不足之处)等等。
它确实依赖于 iconv,但可以在没有该依赖的情况下进行配置。尽管这确实意味着您可以解析的一组可能的文本编码更加有限。
它使用 MIT 许可证。
我不需要完全符合 XML 的要求
好的,完全符合 XML 对您来说并不重要。您的 XML 文档要么完全在您的控制之下,要么保证使用 XML 的“基本子集”:没有命名空间、实体等。
那么什么对你很重要?下一个问题是:在XML工作中,对你来说最重要的事情是什么?
最大 XML 解析性能
应用程序需要采用 XML 并将其转换为 C++ 数据结构,以尽可能快的速度进行此转换。
您已选择:
这个XML解析器正是它在tin上所说的:快速XML。它甚至不处理将文件拉入内存;这是如何发生的取决于你。它处理的是将其解析为一系列您可以访问的 C++ 数据结构。它这样做的速度与逐字节扫描文件所需的速度一样快。
当然,天下没有免费的午餐。与大多数不关心 XML 规范的 XML 解析器一样,Rapid XML 不涉及命名空间、DocType、实体(字符实体和 6 个基本 XML 实体除外)等。所以基本上是节点、元素、属性等。
此外,它是一个 DOM 风格的解析器。因此,它确实需要您阅读所有文本。但是,它不会复制任何文本(通常)。RapidXML 获得最大速度的方式是就地引用字符串。这就需要您进行更多的内存管理(在 RapidXML 查看该字符串时,您必须保持该字符串处于活动状态)。
RapidXML 的 DOM 是基本的。您可以获取事物的字符串值。您可以按名称搜索属性。仅此而已。没有方便的函数可以将属性转换为其他值(数字、日期等)。你只得到字符串。
RapidXML 的另一个缺点是编写 XML 很痛苦。它需要你对字符串名称进行大量显式内存分配,以便构建其 DOM。它确实提供了一种字符串缓冲区,但这仍然需要您进行大量显式工作。它当然是功能性的,但使用起来很痛苦。
它使用 MIT 许可证。它是一个没有依赖项的仅标头库。
- 有一个 RapidXML“GitHub 补丁”,允许它也可以与命名空间一起使用。
我关心性能,但不是那么关心
是的,性能对您很重要。但也许你需要一些不那么简单的东西。也许可以处理更多的Unicode,或者不需要那么多用户控制的内存管理。性能仍然很重要,但你想要一些不那么直接的东西。
您已选择:
从历史上看,这是 RapidXML 的灵感来源。但是这两个项目已经分道扬镳,Pugi提供了更多的功能,而RapidXML则完全专注于速度。
PugiXML 提供 Unicode 转换支持,因此,如果您有一些 UTF-16 文档并希望将它们读取为 UTF-8,Pugi 将提供。如果你需要这种东西,它甚至有一个 XPath 1.0 实现。
但 Pugi 仍然很快。与 RapidXML 一样,它没有依赖关系,并在 MIT 许可证下分发。
阅读大量文档
您需要阅读以千兆字节为单位大小的文档。也许你从stdin中得到它们,由其他一些过程喂养。或者您正在从大量文件中读取它们。或者别的什么。关键是,您需要的是不必一次将整个文件全部读入内存即可对其进行处理。
您已选择:
LibXML2的
Xerces 的 SAX 风格的 API 将以这种能力工作,但 LibXML2 在这里是因为它更容易使用。SAX 样式的 API 是一个推送 API:它开始解析流,然后触发您必须捕获的事件。您必须管理上下文、状态等。读取 SAX 样式 API 的代码比人们想象的要分散得多。
LibXML2 的对象是一个 pull-API。您要求转到下一个 XML 节点或元素;你没有被告知。这允许您以您认为合适的方式存储上下文,以在代码中比一堆回调更具可读性的方式处理不同的实体。xmlReader
选择
Expat 是一个著名的 C++ 解析器,它使用拉取解析器 API。它是由詹姆斯·克拉克(James Clark)撰写的。
它的当前状态为活动。最新版本是 2.2.9,发布于 (2019-09-25)。
它是 StAX 样式 API 的实现。它是一个拉式解析器,类似于 LibXML2 的解析器。xmlReader
但它自 2005 年以来一直没有更新。所以再说一遍,Caveat Emptor。
XPath 支持
XPath 是一个用于查询 XML 树中元素的系统。这是一种使用标准化语法按通用属性有效命名元素或元素集合的便捷方法。许多 XML 库都提供 XPath 支持。
这里实际上有三个选择:
- LibXML2:它提供完整的 XPath 1.0 支持。同样,它是一个 C API,所以如果这困扰您,还有其他选择。
- PugiXML:它还支持 XPath 1.0。如上所述,它比 LibXML2 更像是一个 C++ API,因此您可能更熟悉它。
- TinyXML:它没有 XPath 支持,但有 TinyXPath 库提供它。TinyXML 正在转换为 2.0 版,这极大地改变了 API,因此 TinyXPath 可能无法与新 API 一起使用。与 TinyXML 本身一样,TinyXPath 是在 zLib 许可下分发的。
完成工作
因此,您不关心 XML 的正确性。性能对您来说不是问题。流媒体是无关紧要的。你所需要的只是将 XML 放入内存中并允许你再次将其粘贴回磁盘上的东西。你关心的是 API。
您需要一个小型、易于安装、易于使用且足够小以与最终可执行文件的大小无关的 XML 解析器。
您已选择:
我把TinyXML放在这个插槽中,因为它和XML解析器一样简单易用。是的,它很慢,但它简单明了。它具有许多用于转换属性等的便利功能。
在TinyXML中编写XML没有问题。你只是把一些对象,把它们连接在一起,把文件发给一个,每个人都很高兴。new
std::ostream
还有一个围绕TinyXML构建的生态系统,有一个对迭代器更友好的API,甚至在它上面分层了一个XPath 1.0实现。
TinyXML 使用 zLib 许可证,它或多或少是具有不同名称的 MIT 许可证。
评论
您可能需要考虑另一种处理 XML 的方法,称为 XML 数据绑定。特别是如果您已经有XML词汇表的正式规范,例如,在XML模式中。
XML 数据绑定允许您使用 XML,而无需实际执行任何 XML 分析或序列化。数据绑定编译器自动生成所有低级代码,并将分析的数据呈现为与应用程序域对应的 C++ 类。然后,通过调用函数并使用 C++ 类型(int、double 等)来处理此数据,而不是比较字符串和分析文本(这是使用低级别 XML 访问 API(如 DOM 或 SAX)的操作)。
例如,请参阅我编写的开源 XML 数据绑定实现 CodeSynthesis XSD 和 轻量级、无依赖版本、CodeSynthesis XSD/e。
评论
把我的也放进去。
http://www.codeproject.com/Articles/998388/XMLplusplus-version-The-Cplusplus-update-of-my-XML
没有 XML 验证功能,但速度很快。
评论
在 Secured Globe, 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的另一点说明是:嵌入式系统工作值得一看。但是,您可能在网络上找到的文档是古老且错误的。源代码实际上有相当全面的函数级注释,但需要仔细阅读才能理解它们。
那好吧。我创建了一个新的,因为没有一个列表不能满足我的需求。
好处:
- 拉取解析器流式 API,即解析器就像迭代器一样,没有回调或 DOM 树。即将XML读取到数据结构
- 编译器选项可以关闭异常和 RTTI,错误处理可以通过 std::error_code 完成
- 内存使用限制,支持大文件(使用 100 mib XMark 文件测试,速度取决于硬件)。有一个有限的 COLLADA 格式 3D 模型加载示例
- UNICODE支持,并自动检测输入源编码
评论