序列化 XML 数据
用 XML for C++ 解析器中的 DOMWriter 保存 XML 数据
级别: 中级
Tinny Ng (tng@ca.ibm.com), 系统架构业务方案设计师, IBM 多伦多实验室
2003 年 9 月 01 日
IBM 开发人员 Tinny Ng 向您展示了如何将 XML 数据序列化成具有不同编码的 DOMString。您还会看到一些示例,它们演示如何使用 XML4C/Xerces-C++ 中的 MemBufFormatTarget、StdOutFormatTarget 和 LocalFileFormatTarget 输出流。
Xerces-C++ 是一种用 C++ 编写的 XML 解析器,它由开放源码 Apache XML 项目分发。去年年初起,Xerces-C++ 根据 W3C Document Object Model(DOM)Level 3 Core Specification 和 W3C Document Object Model(DOM)Level 3 Load and Save Specification(请参阅 参考资料)的规定,添加了 W3C 文档对象模型(Document Object Model,DOM)Level 3 子集的实验性实现。
DOM Level 3 Load and Save Specification 定义了一组接口,允许用户将来自于不同输入源的 XML 内容装入和保存到不同的输出流。本文以示例向您展示了如何以这种方式保存 XML 数据。用户可以使这些输出数据流入字符串、内部缓冲区、标准输出或文件。在下列章节中,我将向您展示如何将 XML 数据序列化成具有不同编码的 DOMString
,以及如何使用 Xerces-C++ 中的 MemBufFormatTarget
、 StdOutFormatTarget
和 LocalFileFormatTarget
。
注:IBM XML for C++(XML4C)将 Xerces-C++ 和 International Components for Unicode(ICU)集成到一起,以提供对 100 多种不同编码的支持。在本文档中,我将用 Xerces-C++ 来表示用于 C++ 的 XML 解析器。但是,除非另有指定,否则所描述的行为将同时适用于 XML4C 和 Xerces C++。
DOMBuilder
类提供了用于解析 XML 文档和构建相应 DOM 文档树的 API;而 DOMWriter
类提供了将 DOM 文档序列化(写)到 XML 文档的 API。要序列化 XML 数据,首先使用 DOMBuilder
将 XML 数据装入到 DOM 树,然后使用 DOMWriter
写出 DOM 树。例如:
清单 1. 序列化 XML 数据
// DOMImplementationLS contains factory methods for creating objects // that implement the DOMBuilder and the DOMWriter interfaces static const XMLCh gLS[] = { chLatin_L, chLatin_S, chNull }; DOMImplementation *impl = DOMImplementationRegistry::getDOMImplementation(gLS); // construct the DOMBuilder DOMBuilder* myParser = ((DOMImplementationLS*)impl)-> createDOMBuilder(DOMImplementationLS::MODE_SYNCHRONOUS, 0); // parse the XML data, assume it is saved in a local file // called "theXMLFile.xml" // the DOMBuilder will parse the data and return it as a DOM tree DOMNode* aDOMNode = myParser->parseURI("theXMLFile.xml"); // construct the DOMWriter DOMWriter* myWriter = ((DOMImplementationLS*)impl)->createDOMWriter(); // optionally, set some DOMWriter features // set the format-pretty-print feature if (myWriter->canSetFeature(XMLUni::fgDOMWRTFormatPrettyPrint, true)) myWriter->setFeature(XMLUni::fgDOMWRTFormatPrettyPrint, true); // set the byte-order-mark feature if (myWriter->canSetFeature(XMLUni::fgDOMWRTBOM, true)) myWriter->setFeature(XMLUni::fgDOMWRTBOM, true); // serialize the DOMNode to a UTF-16 string XMLCh* theXMLString_Unicode = myWriter->writeToString(*aDOMNode); // release the memory XMLString::release(&theXMLString_Unicode); myWriter->release(); myParser->release(); |
DOMBuilder
和 DOMWriter
都是使用 DOMImplementationLS
中的工厂方法构造的。在使用完它们后,必需显式地释放它们,以释放任何相关的资源。此外,从 writeToString
返回的字符串归调用程序所拥有,调用程序负责释放所分配的内存。
您也可以选择设置一些控制 DOMWriter
行为的功能。Xerces-C++ 实现了 W3C DOM Level 3 Load and Save Specification 中规定的许多 DOMWriter
功能。可以在 Xerces-C++ 编程指南 DOMWriter
Supported Features(请参阅 参考资料)中找到这些功能的完整列表。其中有两个功能值得着重提一下:
- 格式美化— 这个功能通过添加换行回车符和缩进空格来对输出进行格式化,以生成美化的、可读性良好的格式。因为 W3C DOM Level 3 Load and Save Specification 中没有规定确切的转换格式,所以解析器有它自己的解释。在 Xerces-C++ 2.2(或 XML4C 5.1)之前的发行版中,解析器只对序言和后记进行美化。它不会触及根元素中的内容。但从 Xerces-C++ 2.2(或 XML4C 5.1)开始,启用这个功能也会引起对根元素中内容的格式化。
- 字节顺序标记(byte-order-mark)— 这是 Xerces-C++ 2.2(或 XML4C 5.1)中添加的非标准扩展,用来支持在所生成的 XML 流中写入字节顺序标记(Byte-Order-Mark,BOM)。当且仅当
DOMDocumentNode
是为序列化而生成的时候,BOM 才被写入所生成的 XML 流的开头部分,并且输出编码是下列之一:
- UTF-16
- UTF-16LE
- UTF-16BE
- UCS-4
- UCS-4LE
- UCS-4BE
|
DOMWriter
提供了一个 API,用于将 DOM 节点写入各种类型的输出流中。Xerces-C++ 支持四种输出流类型:
DOMString
MemBufFormatTarget
StdOutFormatTarget
LocalFileFormatTarget
用户可以使用 DOMWriter
的 writeToString
方法将 DOMNode
序列化成 DOMString
(即 Xerces-C++ 中的 XMLCh*
)。这个方法完全忽略所有可用的编码信息,所返回的字符串 总是用 UTF-16 编码的。正如先前提到的,从 writeToString
返回的字符串归调用程序所拥有,调用程序负责释放所有已分配的内存。例如:
清单 2. 将 DOMNode 序列化成 UTF-16 字符串
// construct the DOMWriter DOMWriter* myWriter = ((DOMImplementationLS*)impl)->createDOMWriter(); // serialize a DOMNode to a UTF-16 string XMLCh* theXMLString_Unicode = myWriter->writeToString(*aDOMNode); // release the memory XMLString::release(&theXMLString_Unicode); myWriter->release(); |
如果您打算接收以 UTF-16 之外的其它方式编码的字符串,可以使用 XMLTranscoder
手工转换字符串的编码。使用 XMLPlatformUtils::fgTransService-> makeNewTranscoderFor
构造用于特定编码的 XMLTranscoder
,然后调用 transcodeTo
将 UTF-16 字符串转码成您指定的编码。例如:
清单 3. 将 DOMNode 序列化成 Big5 字符串
// construct the DOMWriter DOMWriter* myWriter = ((DOMImplementationLS*)impl)->createDOMWriter(); // serialize a DOMNode to a UTF-16 string XMLCh* theXMLString_Unicode = myWriter->writeToString(*aDOMNode); // construct a transcoder in Big5 XMLTransService::Codes resCode; XMLTranscoder* aBig5Transcoder = XMLPlatformUtils::fgTransService-> makeNewTranscoderFor("Big5", resCode, 16*1024, XMLPlatformUtils::fgMemoryManager); // transcode the string into Big5 unsigned int charsEaten; char resultXMLString_Encoded[16*1024+4]; aBig5Transcoder->transcodeTo(theXMLString_Unicode, XMLString::stringLen(theXMLString_Unicode), (XMLByte*) resultXMLString_Encoded, 16*1024, charsEaten, XMLTranscoder::UnRep_Throw ); // release the memory XMLString::release(&theXMLString_Unicode); delete aBig5Transcoder; myWriter->release(); |
此处假定与解析器集成在一起的转码器支持您所指定的底层编码。Xerces-C++ 本身支持 ASCII、UTF-8、UTF-16(大/小尾数法,Big/Small Endian)、UCS4(大/小尾数法,Big/Small Endian)、EBCDIC 代码页 IBM037 和 IBM1140、ISO-8859-1(又名 Latin1)以及 Windows-1252。如果您希望支持更多编码(譬如 Shift-JIS 或 Big5),那么您可能会希望使用 XML4C,它将 Xerces-C++ 解析器与 IBM 的 International Components for Unicode(ICU)集成在一起,将所支持的不同编码扩展到 100 多种。
但是, XMLTranscoder
不会更改输入字符串的 XML 声明中所存储的编码信息,该字符串是由 writeToString
所生成的。因此这个以手工方式转码的 XML 字符串的编码属性仍然是“UTF-16”而不是“Big5”。如果您对整个 DOMDocumentNode
进行序列化,而 XML 声明中包括了编码信息的话,这会引起误解。
在这种情况下,要接收非 UTF-16 编码的字符串,建议您使用 MemBufFormatTarget
。
MemBufFormatTarget
将 XML 数据保存到内部缓冲区。 MemBufFormatTarget
在构造时初始化为一个 1023 字节的内存缓冲区,并可根据需要增加。请求时,通过 getRawBuffer()
方法返回以空(null)结束的 XMLByte
流。如果用户打算使返回的缓冲区与 MemBufFormatTarget
的状态无关,他们应该制作自己的返回缓冲区副本。否则,该缓冲区会在破坏 MemBufFormatTarget
时被删除,或者在调用 reset()
函数时被复位。
所返回的 XMLByte
流的编码以如下顺序确定:
- 使用
DOMWriter
中的编码设置 - 如果该设置为空,那么使用将要写入的 DOM 流的编码属性
- 如果上述两处都未提供编码名称,则使用缺省编码 UTF-8
DOMWriter
将在 XML 声明的编码属性中存储正确的编码信息(它与字符串的实际编码相匹配)。
清单 4 说明了如何接收用 Big-5 编码的 XML 字符串:
清单 4. 使用 MemBufFormatTarget
// construct the DOMWriter DOMWriter* myWriter = ((DOMImplementationLS*)impl)->createDOMWriter(); // construct the MemBufFormatTarget XMLFormatTarget *myFormatTarget = new MemBufFormatTarget(); // set the encoding to be Big5 XMLCh tempStr[100]; XMLString::transcode("Big5", tempStr, 99); myWriter->setEncoding(tempStr); // serialize a DOMNode to an internal memory buffer myWriter->writeNode(myFormatTarget, *aDOMNode); // get the string which is encoded in Big 5 from the MemBufFormatTarget char* theXMLString_Encoded = (char*) ((MemBufFormatTarget*)myFormatTarget)->getRawBuffer(); // release the memory myWriter->release(); delete myFormatTarget; |
同样,这也取决于解析器所支持的底层转码能力。如果不支持您所指定的编码,则 DOMWriter
将发出致命错误。
除了将 XML 数据序列化到内部缓冲区之外,还有两种其它类型的输出流: StdOutFormatTarget
和 LocalFileFormatTarget
。
StdOutFormatTarget
将 XML 数据保存到标准输出。例如:
清单 5. 使用 StdOutFormatTarget
// construct the DOMWriter DOMWriter* myWriter = ((DOMImplementationLS*)impl)->createDOMWriter(); // construct the StdOutFormatTarget XMLFormatTarget *myFormatTarget = new StdOutFormatTarget(); // serialize a DOMNode to the standard output myWriter->writeNode(myFormatTarget, *aDOMNode); // release the memory myWriter->release(); delete myFormatTarget; |
LocalFileFormatTarget
将 XML 数据保存到实际的本地文件。在构造 LocalFileFormatTarget
时,用户需要将本地文件名作为参数进行传递。例如:
清单 6. 使用 LocalFileFormatTarget
// construct the DOMWriter DOMWriter* myWriter = ((DOMImplementationLS*)impl)->createDOMWriter(); // construct the LocalFileFormatTarget XMLFormatTarget *myFormatTarget = new LocalFileFormatTarget("myXMLFile.xml"); // serialize a DOMNode to the local file "myXMLFile.xml" myWriter->writeNode(myFormatTarget, *aDOMNode); // optionally, you can flush the buffer to ensure all contents are written myFormatTarget->flush(); // release the memory myWriter->release(); delete myFormatTarget; |
如果该文件尚不存在,则自动创建它。您可以选择在进行任何 I/O 之前刷新(flush)该文件的内容,以确保可以将所有内容写出。
现在,您应该很好地理解了如何将 XML 数据序列化成具有不同编码的不同类型的输出流。这里重申一下,有关更多详细信息,请参阅 W3C DOM Level 3 Load and Save Specification 以及 Xerces-C++ 中的完整 API 文档。
- 您可以参阅本文在 developerWorks 全球站点上的 英文原文.
- 请阅读 W3C Document Object Model(DOM)Level 3.0 Core Specification,它是 W3C 定义的规范,允许程序和脚本动态地访问和更新文档的内容、结构和样式。
- 当您研究本文时,可以查看 W3C DOM Level 3 Load and Save Specification,该规范允许程序和脚本动态地将 XML 文档的内容装入 DOM 文档,并允许将 DOM 文档序列化成 XML 文档。
- 尝试一下 Xerces-C++,这是一种由 Apache 分发的用于 C++ 的 XML 解析器。
- 浏览 IBM alphaWorks 网站,您将看到大量 XML 工具,包括 用于 C++ 的 XML 解析器(XML4C)。
- 研究一下 International Components for Unicode(ICU)库。它们提供了针对各种平台的健壮且功能齐全的 Unicode 服务。XML4C 将 Xerces-C++ 与 ICU 集成到一起以扩展解析器中的编码支持。
- 请参考 Xerces-C++ 编程指南 DOMWriter Supported Features,以获取
DOMWriter
类所支持的所有功能的列表。 - 请查看 Xerces-C++ 中的完整 API 文档,以全面理解 Xerces-C++ API 的工作原理。
- 请查看如何使您成为 IBM 认证的 XML 及相关技术开发人员。
| Tinny Ng 是咨询软件开发人员,目前担任 IBM 多伦多实验室的 WebSphere 系统架构(WebSphere System House)的业务方案解决方案设计师。她原先是 XML for C++ 解析器开发团队的领导,并在两年中带领该团队交付了九个 Apache Xerces-C++ 发行版和七个 IBM XML4C 发行版。Tinny 还领导过 C++ XML 解析器的体系结构设计工作,包括在解析器中重新设计 DOM 实现以获取更高的性能,以及定义新的 DOM C++ 绑定,后来 W3C 引用了这个绑定。她是 Apache XML 开放源码项目 Xerces-C++ 积极的参与者。 |