序列化 XML 数据

用 XML for C++ 解析器中的 DOMWriter 保存 XML 数据

Comments

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++ 中的 MemBufFormatTargetStdOutFormatTargetLocalFileFormatTarget

注:IBM XML for C++(XML4C)将 Xerces-C++ 和 International Components for Unicode(ICU)集成到一起,以提供对 100 多种不同编码的支持。在本文档中,我将用 Xerces-C++ 来表示用于 C++ 的 XML 解析器。但是,除非另有指定,否则所描述的行为将同时适用于 XML4C 和 Xerces C++。

序列化 XML 数据

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();

DOMBuilderDOMWriter 都是使用 DOMImplementationLS 中的工厂方法构造的。在使用完它们后,必需显式地释放它们,以释放任何相关的资源。此外,从 writeToString 返回的字符串归调用程序所拥有,调用程序负责释放所分配的内存。

您也可以选择设置一些控制 DOMWriter 行为的功能。Xerces-C++ 实现了 W3C DOM Level 3 Load and Save Specification 中规定的许多 DOMWriter 功能。可以在 Xerces-C++ 编程指南 DOMWriter Supported Features(请参阅 参考资料)中找到这些功能的完整列表。其中有两个功能值得着重提一下:

  1. 格式美化— 这个功能通过添加换行回车符和缩进空格来对输出进行格式化,以生成美化的、可读性良好的格式。因为 W3C DOM Level 3 Load and Save Specification 中没有规定确切的转换格式,所以解析器有它自己的解释。在 Xerces-C++ 2.2(或 XML4C 5.1)之前的发行版中,解析器只对序言和后记进行美化。它不会触及根元素中的内容。但从 Xerces-C++ 2.2(或 XML4C 5.1)开始,启用这个功能也会引起对根元素中内容的格式化。
  2. 字节顺序标记(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

Xerces-C++ 所支持的输出流

DOMWriter 提供了一个 API,用于将 DOM 节点写入各种类型的输出流中。Xerces-C++ 支持四种输出流类型:

  • DOMString
  • MemBufFormatTarget
  • StdOutFormatTarget
  • LocalFileFormatTarget

DOMString

用户可以使用 DOMWriterwriteToString 方法将 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

MemBufFormatTarget 将 XML 数据保存到内部缓冲区。 MemBufFormatTarget 在构造时初始化为一个 1023 字节的内存缓冲区,并可根据需要增加。请求时,通过 getRawBuffer() 方法返回以空(null)结束的 XMLByte 流。如果用户打算使返回的缓冲区与 MemBufFormatTarget 的状态无关,他们应该制作自己的返回缓冲区副本。否则,该缓冲区会在破坏 MemBufFormatTarget 时被删除,或者在调用 reset() 函数时被复位。

所返回的 XMLByte 流的编码以如下顺序确定:

  1. 使用 DOMWriter 中的编码设置
  2. 如果该设置为空,那么使用将要写入的 DOM 流的编码属性
  3. 如果上述两处都未提供编码名称,则使用缺省编码 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 数据序列化到内部缓冲区之外,还有两种其它类型的输出流: StdOutFormatTargetLocalFileFormatTarget

StdOutFormatTarget

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

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 文档。


相关主题


评论

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

static.content.url=http://www.ibm.com/developerworks/js/artrating/
SITE_ID=10
Zone=XML
ArticleID=21374
ArticleTitle=序列化 XML 数据
publish-date=09012003