??xml version="1.0" encoding="utf-8" standalone="yes"?>
托管代码通过 System.Xml 命名I间q泛支持 XMLQ而依赖于 COM 的传l?Visual Basic] ?C++ 应用E序可以讉K Microsoft] XML 核心服务 (MSXML) 中的cM功能。但是,q些q没有ؓ需要快速精?XML 分析器的本机 C++ 开发h员提供富有吸引力的选项。开始?XmlLite 吧?/p>
本文探讨您可以?XmlLite 执行的操作。但是,首先Qؓ讑֮预期Q我希望快速回一?XmlLite 未提供的内容Q至是此初始版本中未提供的内容。对于初学者,它既未提供文档对象模?(DOM) 实现Q也未提?XML 架构或文档类型定?(DTD) 验证。它q缺对高工具的支持,例如Z光标的导航(?XPathQ、样式表和序列化。但是,通过建立?XmlLite 之上的功能,可以Ҏ需要填补Q何空白,Microsoft .NET Framework 中的几乎所?XML 功能同样都徏立在 XmlReader ?XmlWriter cM上?/p>
那么QXmlLite 提供了哪些内容?单地_它提供了非缓存的只进分析器(提供接收式编E模型)和非~存的只q?XML 生成器。已证明q两者是非常有h值的功能?/p>
开发h员日益熟悉他们每天用的库,通过q泛使用 XMLQ他们肯定会询问有关新推出的 XML 分析器的一些疑N题。要了解q一新分析器的h|让我们首先考虑一?XML 分析器当今的情Ş?/p>
很自然地Q如果应用程序已l利?.NET FrameworkQ则军_通常是很单的Q只需使用 System.Xml 卛_。ؓ证明q一点,XmlLite 的设计基?.NET Framework ?XmlReader ?XmlWriter cȝ设计。从?C++ ~写的托应用程序?XmlLite 通常没有优势。XmlLite 的功能毕竟比 XmlReader ?XmlWriter cL供的功能要少得多。(图? 中的表略q?XmlLite 中的主要cd如何映射?.NET Framework 中的主要cd。)另一斚wQ如果应用程序仅使用本机代码Q那么就 Microsoft 技术而言QMSXML 在传l上是所选的解决Ҏ?/p>
MSXML 提供了两个差异很大的 XML 分析器。第一个分析器是在各种情Ş下可用的 DOM 实现。如果用较的 XML 文档且需要随?XML 文档q行内存中读取和写入Q则 DOM 实现是一U合理的选择。MSXML 的更高版本引入了“用?XML 的简?API (SAX2)”的实现。它实际上是否简单是有争议的。?SAX2 Ӟ甚至在开始之前)Q您需要实现至两?COM 接口Q一个用于接?XML 文档中各个节点的通知Q另一个用于接收分析错误的通知?/p>
?SAX2 实现d?MSXML 的原因如下:?DOM 实现不同QSAX2 分析器以数据Ş式读?XML 文档Qƈ通知您何时到辑个节炏V这意味着Q您的应用程序的内存使用量ƈ不随所分析文档的大而增加?/p>
SAX2 存在的问题以?.NET Framework 不提供其实现的原因在?SAX2 模型的内在复杂性。它要求实现接口或事Ӟq强制开发h员用更为间接的~程模型Q要求开发h员管理注定会使应用程序变得复杂的其他状态。相反,.NET Framework 中的 XmlReader ?XmlWriter cM?XmlLite ?IXmlReader ?IXmlWriter 接口提供了简单易懂的分析器,可以直接在函C使用Q而不必管理Q何外部状态或通知?/p>
׃其设计的明性,XmlLite 能够提供相当好的性能Q即使与 MSXML SAX2 实现相比也是如此。虽?SAX2 分析器可以比 DOM 实现更好地处理大型文档,但是?XmlLite 相比逊色了?/p>
单地_XmlLite 优于 MSXMLQ且它更易于从本?C++ 使用。MSXML 仍将?Visual Basic 和基?COM 的脚本语a的最可行解决ҎQ但是现在本?Visual C++] 最l具有了专门为它设计?XML 分析器。虽?Windows Vista?和更高版本中附带?XmlLiteQ但是一个更新对?Windows] XP ?Windows Server] 2003 ?32 位和 64 位版本也是可用的。因为未涉及 COM 注册Q所以此更新包应该不会导?MSXML 通常造成的有兛_装和版本控制的难题?/p>
XmlLite 不仅是易记的名称Q事实上Q它是一个轻?XML 分析器。XmlLite 利用?COM 的精华,即编E规范和U定Qƈ抛弃了复杂的和可能不必要的部分,?COM 注册、运行时服务、代理、线E模型、封送处理等?/p>
?XmlLite.dll 导出的函数创?XML d器和写入器。通过链接?XmlLite.lib q包?Windows SDK 中的 XmlLite.h 头文Ӟ可以讉K它们。生成的 COM 样式接口使用熟悉?IUnknown 接口Ҏq行生存期管理。COM IStream 接口也vC定作用ƈ表示存储器。除此之外,没有 COM 的依赖项Q无需注册M COM cL甚至调用强制性的 CoInitialize 函数。活动模板库 (ATL) CComPtr cd理剩余的一部?COM。但是,您确实需要关注线E安全,因ؓZ单线E方案中的性能QXmlLite 不是U程安全的?/p>
我在以下CZ中?COM_VERIFY 宏,以便清晰地识别方法在何处q回需要检查的 HRESULT。可以将此替换ؓ相应的错误处?- 不管该操作引发异常还是您自己q回 HRESULT?/p>
XmlLite 提供了返?IXmlReader 接口实现?CreateXmlReader 函数Q?
虽然是可选的Q但?CComPtr cL板确保迅速释放接口指针?/p>
CreateXmlReader 接受接口标识W?(IID) 以及指向 void 指针的指针。这?COM ~程中的常见模式Q允许调用方指定要返回的接口指针的类型。我的示例?__uuidof q算W,该运符?Microsoft 特定的关键字Q用于提取与cd兌?GUID。在q种情况下,它用于检索接口的 IID。CreateXmlReader 的最后一个参数接受可选的 IMalloc 实现以允许调用方控制内存分配?/p>
创徏d器后Q需要指C取器用作输入的存储器。IStream 接口表示存储器,q样可以将 XmlLite 与可能设计的M实C起用:
Q我在本文的后面部分中讨论。) 讄 XML d器的输入后,可以通过重复调用 Read Ҏq行d。Read Ҏ接受一个可选参敎ͼ该参数在每次成功调用时返回节点类型。Read Ҏq回 S_OK 以指C已从流中成功读取下一个节点,q回 S_FALSE 以指C已到达的l尾处。以下是如何依次枚D节点的一个示例:
要枚丑ֽ前节点的属性,请?MoveToFirstAttribute ?MoveToNextAttribute Ҏ。如果已成功地重新定位读取器Q则q两U方法都q回 S_OKQ如果不存在更多的属性,则返?S_FALSE。以下示例说明如何依ơ枚丄定节点的属性:
调用 IXmlReader ?Read ҎӞ它会Q何节点属性自动存储在内部集合中。这P您就可以使用 MoveToAttributeByName ҎQ按名称读取器Ud到特定的属性。但是,枚D属性ƈ其存储在应用程序特定的数据l构中,效率通常更高。请注意Q您q可以?GetAttributeCount Ҏ定当前节点中的属性数?/p>
定节点或属性后Q获取其信息很单了。以下示例演C如何获取给定节点的命名I间 URI 和本地名Uͼ
q回字符串值的所?IXmlReader Ҏ都遵循此模式。第一个参数接受指向宽字符指针帔R的指针。第二个参数是可选的Q如果它不ؓӞ则它返回以字符度量的字W串长度Q不包括I结束符Q?/p>
以下是强调性能的另一个示例。仅在将d器移动到其他节点或以某种其他方式Q如通过讄新的输入或释放 IXmlReader 接口Q当前节点无效之前Q从 IXmlReader Ҏq回的字W串指针才是有效的。换句话_IXmlReader 不会流的副本返回给调用斏V?/p>
与其?.NET Framework 中的对应方不同,IXmlReader 未提供读取键入内容的MҎ。例如,如果特定的元素或属性包含数字或日期Q则您需要首先获取其字符串表CŞ式,然后Ҏ需要自p行{换?NET Framework ?XmlReader cM存在的许多其?helper Ҏ也不存在?IXmlReader 中,但是可以作ؓ helper 函数~写。XmlLite 实W合最接口设计的 C++ 理论?/p>
图? 昄使用 IXmlReader d XML 文档时涉及的对象和抽象。但是,L讎ͼIStream 可以抽取M存储Q此处显C的文g仅仅是一个常见示例?/p>
XmlLite 提供了返?IXmlWriter 接口实现?CreateXmlWriter 函数Q?
创徏写入器后Q需要指C写入器用作输出的存储器:
开始写入之前,可以修改写入器属性。XmlWriterProperty 枚D定义可用的属性。例如,您可能希望指定是否羃q?XML 输出以便于读者阅读(使用 SetProperty Ҏ可以做到q一点)Q?
然后可以开始?IXmlWriter Ҏ写入基础。XmlLite 支持 XML 片段。如果计划写入完整的 XML 文档Q则应该从调?WriteStartDocument ҎQ它负责写入 XML 声明Q开始。声明取决于所用的~码Q但是默认编码ؓ UTF-8Q它在大多数情况下都应该是合适的。(E后介l文本编码。)提供了许?WriteXxx ҎQ用于写入各U节点类型、属性和倹{?/p>
误虑以下CZQ?
WriteStartDocument Ҏ处理?XML 声明写入的操作。它只有一个参敎ͼ该参数接受来?XmlStandalone 枚D的|指示是否出现独立的文档声明,如果是这P则指C它保存的倹{写?XML 片段Ӟ通常省略?WriteStartDocument 的调用?/p>
WriteStartElement Ҏ接受以下三个参数Q第一个参数指定元素的可选命名空间前~Q第二个参数指定元素的本地名UͼW三个参数指定可选的命名I间 URI。WriteElementString ?XmlLite 提供的非常方便的Ҏ之一。用于写?XHTML 文档标题的以下代码等效于上一CZ中用的 WriteElementStringQ?
昄QWriteElementString Ҏ不是l对必要的,但它实很有用?/p>
最后,WriteEndDocument Ҏ用于关闭文档。您可能已注意到Q未昑ּ关闭 body ?html 元素。WriteEndDocument 会自动关闭Q何打开的元素。就此而言Q释攑ֆ入器也会关闭M剩余的元素。但是,如果您不心Q则未显式关闭此cd素的做法可能会导致错误,因ؓ的生存期和写入器的生存期通常可以不同。要说的是,如果需要确保已所有要写入的内容写入基,则只需调用 IXmlWriter ?Flush Ҏ卛_?/p>
图? 昄使用 IXmlWriter 写入 XML 文档时涉及的对象和抽象流。请牢记QIStream 可以抽取M存储Q此处的文g仅仅是一个常见示例?/p>
到此为止Q我Ҏq行的介lƈ不多。与一些功能更全面?XML 库不同,XmlLite 未提供Q何支持从公共存储位置Q如文g或通过|络协议Q读取和向其写入的功能。正因ؓq一点,对于希望从其d或向其写入的M存储器,您都需要提?IStream 实现。实?IStream 接口q不复杂Q但是在许多情况下,您不需要执行此操作Q因为实现可能已存在?/p>
CreateStreamOnHGlobal 函数提供p拟内存支持的 IStream 实现。第一个参数是使用 GlobalAlloc 函数创徏的可选内存句柄。但是,只需传递零QCreateStreamOnHGlobal 卛_为您创徏内存对象。以下示例创Z个由pȝ内存支持且将Ҏ需要动态增长的 IStream 实现Q?
释放将释放内存?/p>
SHCreateStreamOnFile 函数提供了另一个有用的 IStream 实现。它创徏由文件支持的 IStreamQ?
虽然默认情况?XmlLite 使用 UTF-8 q行写入Q但是如果在d时尝试检文本编码,则可以覆盖此行ؓ。首先,让我们看一下您自动获取的信息。对于给定的,IXmlReader 通过作ؓ XML 前同步码的字节顺序标记来编码提C。IXmlReader q将允许?XML 声明中指定的M~码。期望Q?XML 分析器都hq两个特征。如果具有可能未定义M~码信息的输入流Q而且 XmlLite 无法试探性地定正用的~码Q则可以?IXmlReader 定向到特定的~码Q如果给定了代码|~码名称Q?/p>
可以假?IXmlReaderInput 接口创徏 XML d器输入对象,而不是将直接传递到 IXmlReader。提供了两个用于创徏包装输入的输入对象的函数。CreateXmlReaderInputWithEncodingCodePage 函数接受代码늼号Ş式的代码。CreateXmlReaderInputWithEncodingName 函数接受使用其规范名U的~码。除此之外,q两个函数具有完全相同的{。概括一下,通常可以?XML d器的输入进行如下设|:
要覆盖编码,请将代码更改为:
W一个参数指C?XML d器将从其d的流。第二个参数接受可选的 IMalloc 实现。如果提供的话,则它覆?XML d器自q实现。第三个参数指定~码名称?a >msdn2.microsoft.com/ms752827.aspx 上的文档列出了本机支持的~码Q要支持其他~码Q可以提?IMultiLanguage2 接口实现。下一个参数指C是否必M用指定的~码或者它是否仅仅是一个提C。如果指?TRUEQ则指示分析器尝试用徏议的~码Q但是如果它p|Q则可以随意试试探性地定实际的编码。如果指?FALSEQ则指示分析器尝试徏议的~码Q如果它与输入流不匹配,则返回错误。下一个参数接受可能用于解析外部实体的可选基?URI。最后一个参数返回表C传递到 SetInput Ҏ的输入对象的接口指针?/p>
XML 写入器将Z传递到 SetOutput Ҏ的对象确定要使用的编码。如果该对象实现 IStream 接口或者甚臛_现有限的 ISequentialStream 接口Q则 XML 写入器将使用 UTF-8 ~码。可以创?XML 写入器输出对象来覆盖此行为。提供了两个用于创徏包装输出的输出对象的函数。CreateXmlWriterOutputWithEncodingCodePage 函数接受代码늼号Ş式的~码Q?CreateXmlWriterOutputWithEncodingName 函数接受使用其规范名U的~码。除此之外,q两个函数具有完全相同的{。通常Q可以对 XML 写入器的输出进行如下设|:
要覆盖默认编码,L写以下代码:
W一个参数指C?XML 写入器将写入的流。第二个参数接受可选的 IMalloc 实现。如果提供的话,则它覆?XML 写入器自q实现。第三个参数指定~码名称。最后一个参数返回表C传递到 SetOutput Ҏ的输出对象的接口指针?/p>
Z在读取大数据值时限制内存使用QXML d器提供了按数据块d值的机制。IXmlReader ReadValueChunk Ҏd的字W数不超q规定的最大字W数Q在预料到后l调用时向前Udd器。以下示例说明如何重复调?ReadValueChunk 以读取大数据|
当不再有数据可用ӞReadValueChunk q回 S_FALSE。在此示例中Q我要将数据块写?CString 对象。这仅仅是ؓ了说明如何管理数据块的长度,昄q在实际中会抉|数据分块的优ѝ?/p>
?XML Z心的应用E序必须L处理来自不可信源?XML。XmlLite 提供了许多工具以保护应用E序免受已知漏洞和将来漏z的d?/p>
XML 文档可以包含对外部实体的引用。一?XML 分析器自动解析这些实体。虽然此Ҏ可能很有用,但是Q如果未仔细~写 XML 解析E序以缓解各U威胁,则此Ҏ可能会造成安全漏洞的攻凅RXmlLite 既不自动解析外部实体Q也不提?XML 解析E序。要提供自己的实玎ͼ如有必要Q,请实?IXmlResolver 接口q将 XmlReaderProperty_XmlResolver 属性与 IXmlReader SetProperty Ҏ一起用,以指C取器使用您的解析E序?/p>
XML 文档可能q包?DTD 处理说明。虽?XmlLite 不支持文档验证(使用 XML 架构?DTDQ,但是它支?DTD 实体扩展和默认属性。由于这?DTD 可以包含对外部实体的引用Q因此它们可能会使您的应用程序受到各U攻凅R默认情况下QXmlLite 用 DTD 处理。通过?XmlReaderProperty_DtdProcessing 属性设|ؓ DtdProcessing_Parse |可以允许 DTD 处理。此外,q存在由 XmlReaderProperty_MaxEntityExpansion 控制?DTD 实体扩展dQ也UCؓ billion laughs dQ的内置~解措施。此属性的默认gؓ 100,000?/p>
d者可以利用?XML 的应用程序的另一U方法是Q创建名U非帔R的文档。如果未能阻止,则这可能用尽巨大的内存ƈ允许拒绝服务d。我已经提示了可以执行的Ҏ。缓解此cd胁的一U明显方法是Q按数据块读取大数据|如上一部分所q。另一U有用的Ҏ是,提供限制内存分配的自定义 IMalloc 实现。如果输入流支持随机讉KQ则q可以指C?XML d器?XmlReaderProperty_RandomAccess 属性来避免~存属性。这减用于读取开始元素标记的内存量,但是也可能降低分析速度Q因为分析器必须来回查找以便在请求时索各个属性倹{?/p>
如果 XML 层次l构q深Q则也可能快速用系l资源。要Ld者提供层ơ结构过q XML 文档Q可以?XmlReaderProperty_MaxElementDepth 属性限制分析器允许的深度。此属性默认ؓ 256?/p>
XmlLite 为本?C++ 应用E序提供了功能强大的 XML 分析器。它着重于性能Q知道它所使用的系l资源,为控制这些特征提供了很大的灵zL。XmlLite 支持所有的常见文本~码Q是一U非常有用的实用工具Q可以简化本?C++ 应用E序中的 XML 使用。有兌l信息,请参?msdn2.microsoft.com/ms752872.aspx 上的 XmlLite 文档?/p>
管 .NET Framework 不断取得成功QMicrosoft 仍然认真对待本机 C++ 开发。通过引入 XmlLiteQ适合于用本机 C++ ~写的应用程序的高性能、低开销 XML d器和写入器)说明了这一炏V?
Z么推出新?XML 分析器?
COM“Lite?/span>
d XML
CComPtr<IXmlReader> reader;
COM_VERIFY(::CreateXmlReader(__uuidof(IXmlReader),
reinterpret_cast<void**>(&reader),
0));
CComPtr<IStream> stream;
// Create stream object here...
COM_VERIFY(reader->SetInput(stream));
HRESULT result = S_OK;
XmlNodeType nodeType = XmlNodeType_None;
while (S_OK == (result = reader->Read(&nodeType)))
{
// Get node-specific info
}
for (HRESULT result = reader->MoveToFirstAttribute();
S_OK == result;
result = reader->MoveToNextAttribute())
{
// Get attribute-specific info
}
PCWSTR namespaceUri = 0;
UINT namespaceUriLength = 0;
COM_VERIFY(reader->GetNamespaceUri(&namespaceUri,
&namespaceUriLength));
PCWSTR localName = 0;
UINT localNameLength = 0;
COM_VERIFY(reader->GetLocalName(&localName,
&localNameLength));
?2 d?/span>
写入 XML
CComPtr<IXmlWriter> writer;
COM_VERIFY(::CreateXmlWriter(__uuidof(IXmlWriter),
reinterpret_cast<void**>(&writer),
0));
CComPtr<IStream> stream;
// Create stream object here
COM_VERIFY(writer->SetOutput(stream));
COM_VERIFY(writer->SetProperty(XmlWriterProperty_Indent, TRUE));
COM_VERIFY(writer->WriteStartDocument(XmlStandalone_Omit));
COM_VERIFY(writer->WriteStartElement(0, L"html",
L"http://www.w3.org/1999/xhtml"));
COM_VERIFY(writer->WriteStartElement(0, L"head", 0));
COM_VERIFY(writer->WriteElementString(0, L"title", 0, L"My Web Page"));
COM_VERIFY(writer->WriteEndElement()); // </head>
COM_VERIFY(writer->WriteStartElement(0, L"body", 0));
COM_VERIFY(writer->WriteElementString(0, L"p", 0, L"Hello world!"));
COM_VERIFY(writer->WriteEndDocument());
COM_VERIFY(writer->WriteStartElement(0, L"title", 0));
COM_VERIFY(writer->WriteString(L"My Web Page"));
COM_VERIFY(writer->WriteEndElement());
?3 写入?/span>
使用?/span>
CComPtr<IStream> stream;
COM_VERIFY(::CreateStreamOnHGlobal(0, TRUE, &stream));
CComPtr<IStream> stream;
COM_VERIFY(::SHCreateStreamOnFile(L"D:\\Sample.xml",
STGM_WRITE | STGM_SHARE_DENY_WRITE,
&stream));
d时的文本~码
CComPtr<IStream> stream;
// Create stream object here
COM_VERIFY(reader->SetInput(stream));
CComPtr<IStream> stream;
// Create stream object here
CComPtr<IXmlReaderInput> input;
COM_VERIFY(::CreateXmlReaderInputWithEncodingName(stream,
0, // default allocator
L"ISO-8859-8",
TRUE, // hint
0, // base URI
&input));
COM_VERIFY(reader->SetInput(input));
写入时的文本~码
CComPtr<IStream> stream;
// Create stream object here
COM_VERIFY(writer->SetOutput(stream));
CComPtr<IStream> stream;
// Create stream object here
CComPtr<IXmlWriterOutput> output;
COM_VERIFY(::CreateXmlWriterOutputWithEncodingName(stream,
0,
L"ISO-8859-8",
&output));
COM_VERIFY(writer->SetOutput(output));
处理大数据?/span>
CString value;
WCHAR chunk[256] = { 0 };
HRESULT result = S_OK;
UINT charsRead = 0;
while (S_OK == (result = reader->ReadValueChunk(chunk,
countof(chunk),
&charsRead)))
{
value.Append(chunk, charsRead);
}
安全注意事项
ȝ
short 臛_ 16?br />int 臛_和short 一样长
long 臛_32位,且至要和int 一样长?br />
无符L本??前面d关键字?unsigned
unsigned short unsigned int unsigned long ,如果 只是 unsigned 是 unsigned int 的简写?br />