![]() 使用 MSXML 分析器处理 XML 文档发布日期: 8/6/2004 | 更新日期: 8/6/2004
Kenn Scribner 在 Kenn Scribner 近期有关 XML 和 MSXML DOM 分析器的文章中,仅介绍了该分析器的部分功能。这些文章将 XML 作为一种技术进行了说明,但是并没有介绍 XML 分析器本身。现在,Kenn 将回过头来介绍 MSXML 分析器,并讲解处理 XML 文档和节点所需的基本知识:搜索特定的节点、插入节点和检索节点值。 ![]() MSXML 分析器基于 XML 文档对象模型,对于查看表 1 中所示的各种文档对象来说,它非常重要。这些对象直接出自 XML 规范本身。MSXML 还可以进一步将 XML DOM 对象合并到 COM 中。因此,弄清楚哪个 XML DOM 对象对应于哪个 MSXML COM 接口非常容易。例如,IXMLDOMNode 代表称为 Node 的 DOM 对象。
虽然有时比较容易混淆,但是 XML 文档对象可以是(并且通常是)多态的。即,“节点”同时也是一个“元素”。当您试图确定需要何种 DOM 对象来执行何种操作时,这有时会造成混淆。可以使用“文档”对象来创建 DOM“节点”,但是,如果要向新创建的节点添加属性,就必须通过其作为“元素”的一面来访问它。如果说存在一种将对象和操作关联在一起的神奇模式,那么我还没能从自己的日常工作中将它提炼出来。我发现自己仍需要不断参考 MSDN 文档来查看哪个 COM 接口提供了所需的方法以执行我试图完成的任务。各种对象方法看上去的确是按逻辑分组的,这也正是我对 DOM 当初的开发模式的推断(通过分组逻辑操作)。 因此,其中的诀窍就在于从 MSXML 分析器检索适当的 DOM 对象,这一操作的具体实现就是 COM 对象。操作的基本模式将是:首先实例化 MSXML COM 对象本身的一个副本,然后从该副本请求或以其他方式获取指向附加 XML DOM 对象(本身也是 COM 对象)的指针。 MSXML DOM 试验应用程序创建一个漂亮的应用程序,演示众多的 MSXML 功能,这很简单,但实际上,附加的代码只会画蛇添足。相反,我选择了开发一个简单的基于控制台的应用程序,该应用程序执行四种基本操作:
为了进一步简化,我硬编码了 XML 文档文件的名称和 XML 节点本身。当然,如果这是一个真实的应用程序,您可能很少(或者永远不会)采用这样的方法。但是在本例中,进行这些权衡,是为了简化围绕在 MSXML 功能两边的代码。 像平常一样,在示例应用程序中,我选择了使用 ATL 来包装许多与 COM 有关的活动。您肯定看到我使用了 CComPtr 和 CComQIPtr 对象,但是我还额外加入了几个 CComBSTR 和 CComVariant 对象。如果您不熟悉它们,只需要记住它们是用于处理一些细节的模板,这些细节对于本文的主旨来说并非至关重要,但是从更广的角度讲,还是比较重要的。真正重要的是看到如何搜索 XML 节点,添加新的(具有属性的)节点,以及显示节点内包含的文本。 我的基于控制台的应用程序可以在附带的 下载文件中找到,它将加载一个名为 xmldata.xml 的 XML 文档文件(假定其与可执行文件位于同一个目录中),并假定该文档包含以下 XML 数据: <?xml version="1.0"?> <xmldata> <xmlnode /> <xmltext>Hello, World!</xmltext> </xmldata> 我们将首先搜索 xmlnode 节点,如果找到了该节点,我们将插入一个新的(带有属性的)节点作为其子级。生成的 XML 文档将为: <?xml version="1.0"?> <xmldata> <xmlnode> <xmlchildnode xml="fun" /> </xmlnode> <xmltext>Hello, World!</xmltext> </xmldata> 打印 节点内包含的信息 ("Hello, World!") 之后,我们将把该新 XML 文档保存到名为 updatedxml.xml 的文件中。然后,就可以使用文本编辑器或 Internet Explorer 5.x 来查看结果。现在让我们转到代码。 应用程序首先初始化了 COM 运行库,然后创建了 MSXML 分析器的一个实例: CComPtr<IXMLDOMDocument> spXMLDOM; HRESULT hr = spXMLDOM.CoCreateInstance( __uuidof(DOMDocument)); if ( FAILED(hr) ) throw "Unable to create XML parser object"; if ( spXMLDOM.p == NULL ) throw "Unable to create XML parser object"; 如果创建分析器实例成功,接下来,我们将把 XML 文档加载到分析器中: VARIANT_BOOL bSuccess = false; hr = spXMLDOM->load(CComVariant(L"xmldata.xml"), &bSuccess); if ( FAILED(hr) ) throw "Unable to load XML document into the parser"; if ( !bSuccess ) throw "Unable to load XML document into the parser"; 搜索节点与文档对象有关,因此,我们将使用 IXMLDOMDocument::selectSingleNode() 来根据其名称查找特定的 XML 节点。其他的技巧很多,但是如果准确地知道要查找的节点的名称,这是最直接的方法: CComBSTR bstrSS(L"xmldata/xmlnode"); CComPtr<IXMLDOMNode> spXMLNode; hr = spXMLDOM->selectSingleNode(bstrSS,&spXMLNode); if ( FAILED(hr) ) throw "Unable to locate ‘xmlnode‘ XML node"; if ( spXMLNode.p == NULL ) throw "Unable to locate ‘xmlnode‘ XML node"; 一些您应当了解的其他方法包括 IXMLDOMDocument::nodeFromID() 和 IXMLDOMElement::getElementsByTagName(),使用它们可以获得文档中的节点的列表。您还可以将文档作为树来进行访问,并依次通过它(获取子节点,获取同辈节点等)。 任一种情况下,搜索的结果都是一个 MSXML 节点对象 IXMLDOMNode。文档中必须存在该节点,否则搜索将失败。我的应用程序使用该节点作为一个全新 XML 节点的父级,该新节点是由 XML 文档对象创建的: CComPtr<IXMLDOMNode> spXMLChildNode; hr = spXMLDOM->createNode(CComVariant(NODE_ELEMENT), CComBSTR("xmlchildnode"), NULL, &spXMLChildNode); if ( FAILED(hr) ) throw "Unable to create ‘xmlchildnode‘ XML node"; if ( spXMLChildNode.p == NULL ) throw "Unable to create ‘xmlchildnode‘ XML node"; 如果分析器可以创建该节点,下一步就是将它放到 XML 树中。IXMLDOMNode::appendChild() 正是完成这一任务的方法: CComPtr<IXMLDOMNode> spInsertedNode; hr = spXMLNode->appendChild(spXMLChildNode, &spInsertedNode); if ( FAILED(hr) ) throw "Unable to move ‘xmlchildnode‘ XML node"; if ( spInsertedNode.p == NULL ) throw "Unable to move ‘xmlchildnode‘ XML node"; 如果父节点的确将新创建的节点插入为其子级,将返回另一个 IXMLDOMNode 实例,该实例表示新的子节点。实际上,该新子节点和传递给 appendChild() 的节点是同一个 XML 节点。由于在存在问题时附加的子节点的指针将为 Null,因此,检查该指针很有用。 到目前为止,我找到了一个特定的节点,并为它创建了一个新的子节点,下面,让我们看看如何处理属性。假定您要将该属性添加到新的子节点: xml="fun" 这并不难,但是您必须从 IXMLDOMNode 切换到 IXMLDOMElement,以便访问该子节点的元素特征。在实践中,这意味着您必须查询 IXMLDOMNode 接口的相关 IXMLDOMElement 接口,查明后,再调用 IXMLDOMElement::setAttribute(): CComQIPtr<IXMLDOMElement> spXMLChildElement; spXMLChildElement = spInsertedNode; if ( spXMLChildElement.p == NULL ) throw "Unable to query for ‘xmlchildnode‘ XML _ element interface"; hr = spXMLChildElement->setAttribute(CComBSTR(L"xml"), CComVariant(L"fun")); if ( FAILED(hr) ) throw "Unable to insert new attribute"; 此时,已经修改了 XML 树,并创建了所需的树。应用程序可以在这个时候将文档保存到磁盘,或者执行其他任务。现在,让我们来搜索另一个节点并显示该节点所包含的值(文本)。您已经了解了如何搜索节点,因此,我们将直接讲解数据提取。 提取节点数据的关键在于使用 IXMLDOMNode::get_nodeTypedValue()。可以使用 Microsoft 数据类型架构来标识节点所包含的数据,因此可以方便地存储浮点值、整数、字符串或该架构所支持的任何数据类型。可以使用 dt:type 属性来指定数据类型,如下所示: <model dt:type="string">SL-2</model> <year dt:type="int">1992</year> 如果特定的节点具有指定的数据类型,就可以使用 get_nodeTypedValue() 以该格式提取数据。如果未指定数据类型,将假定数据为文本,分析器将返回具有 BSTR 数据的 VARIANT。在本例中,这没有任何问题,因为我们要搜索的节点是一个实际上包含一个字符串的文本节点。在需要时,始终可以使用 atoi() 等方法将字符串转换为其他形式。本例中,我们只是提取该字符串数据并显示它: CComVariant varValue(VT_EMPTY); hr = spXMLNode->get_nodeTypedValue(&varValue); if ( FAILED(hr) ) throw "Unable to retrieve ‘xmltext‘ text"; if ( varValue.vt == VT_BSTR ) { // Display the results...since we‘re not using the // wide version of the STL, we need to convert the // BSTR to ANSI text for display... USES_CONVERSION; LPTSTR lpstrMsg = W2T(varValue.bstrVal); std::cout << lpstrMsg << std::endl; } else { // Some error throw "Unable to retrieve ‘xmltext‘ text"; } 如果能够检索与节点关联的值,并且该值为 BSTR(预期的数据类型),我们将在屏幕上显示该文本。如果不能,将显示一条错误消息,不过,根据情况而定,可以方便地采取其他操作。 最后一项与 XML 有关的操作是将已更新的 XML 树保存到磁盘,这一任务是使用 IXMLDOMDocument::save() 完成的: hr = spXMLDOM->save(CComVariant("updatedxml.xml")); if ( FAILED(hr) ) throw "Unable to save updated XML document"; 完成保存后,向屏幕写一条简短说明,并退出。 这个示例应用程序无论如何都算不上漂亮。您可以让自己的应用程序执行很多其他功能,但我希望您通过这个简短的示例了解到了如何从 C++ 程序使用 MSXML 分析器。该分析器本身是一个复杂的软件,无论怎样强调使用 MSDN Library 作为参考,都不能算是过份。该分析器公开了许多接口,这些接口通常会公开许多方法。即便如此,我在自己的项目中仍频繁地使用该分析器,在亲自编写了一些代码并进行试验后,我发现这个软件制作很精良 并且便于使用。我希望您也同样会发现该分析器和一般意义上的 XML 具有广泛的用途。 要了解有关 Visual C++ Developer 和 Pinnacle Publishing 的更多信息,请访问他们的 Web 站点,网址为: http://www./ 注:这不是 Microsoft Corporation 的网站。Microsoft 对该网站内容不承担责任。 本文复制自 Visual C++ Developer 的 2000 年 11 月刊。版权所有 2000,Pinnacle Publishing, Inc.(除非另行说明)。保留所有权利。Visual C++ Developer 是 Pinnacle Publishing, Inc. 独立发行的产品。未经 Pinnacle Publishing, Inc. 事先同意,不得以任何形式使用或复制本文的任何部分(评论文章中的简短引用除外)。要联系 Pinnacle Publishing, Inc.,请致电 1-800-788-1900。 |
|
来自: shaolong007 > 《XML》