【问题标题】:MSXML Select Nodes Not WorkingMSXML 选择节点不工作
【发布时间】:2010-09-22 00:47:39
【问题描述】:

我正在开发一个自动化测试应用程序,目前正在编写一个函数来比较两个应该相同但可能不同的 XML 文件之间的值。这是我正在尝试处理的 XML 示例:

<?xml version="1.0" encoding="utf-8"?>
<report xmlns="http://www.**.com/**">
  <subreport name="RBDReport">
    <record rowNumber="1">
      <field name="Time">
        <value>0</value>
      </field>
      <field name="Reliability">
        <value>1.000000</value>
      </field>
      <field name="Unreliability">
        <value>0.000000</value>
      </field>
      <field name="Availability">
        <value> </value>
      </field>
      <field name="Unavailability">
        <value> </value>
      </field>
      <field name="Failure Rate">
        <value>N/A</value>
      </field>
      <field name="Number of Failures">
        <value> </value>
      </field>
      <field name="Total Downtime">
        <value> </value>
      </field>
    </record>

(注意可能有多个 &lt;subreport&gt; 元素,在这些元素中,还有多个 &lt;record&gt; 元素。)

我想要的是提取两个文档的&lt;value&gt; 标签,然后比较它们的值。那部分我知道该怎么做。问题在于提取本身。

由于我被困在 C++ 中,我正在使用 MSXML,并编写了一个包装器以允许我的应用程序抽象出实际的 XML 操作,以防我决定更改我的数据格式。

包装器 CSimpleXMLParser 加载 XML 文档并将其“顶部记录”设置为 XML 文档的文档元素。 (CRecord 是一个抽象类,CXMLRecord 是其子类之一,它可以单独或按组访问子记录,还允许访问记录的“值”(在 CXMLRecord 的情况下,子元素或属性的值) .) CXMLRecord 包含一个 MSXML::MSXMLDOMNodePtr 和一个指向 CSimpleXMLParser 实例的指针。) 包装器还包含用于返回子项的实用函数,CXMLRecord 使用这些函数返回其子记录。

在我的代码中,我执行以下操作(尝试返回所有 &lt;subreport&gt; 节点以查看它是否有效):

CSimpleXMLParser parserReportData;
parserReportData.OpenXMLDocument(strPathToXML);
bool bGetChildrenSuccess = parserReportData.GetFirstRecord()->GetChildRecords(listpChildren, _T("subreport"));

这总是返回 false。 CXMLRecord::GetChildRecords() 的实现内容基本上是

MSXML2::IXMLDOMNodeListPtr pListChildren = m_pParser->SelectNodes(strPath, m_pXMLNode);

if (pListChildren->Getlength() == 0)
{
    return false;
}

for (long l = 0; l < pListChildren->Getlength(); ++l)
{
    listRecords.push_back(new CXMLRecord(pListChildren->Getitem(l), m_pParser));
}

return true;

而 CSimpleXMLParser::SelectNodes() 是:

MSXML2::IXMLDOMNodeListPtr CSimpleXMLParser::SelectNodes(LPCTSTR strXPathFilter, MSXML2::IXMLDOMNodePtr pXMLNode)
{
    return pXMLNode->selectNodes(_bstr_t(strXPathFilter));
}

运行时,肯定会正确地将最高记录设置为&lt;report&gt; 元素。我可以用它做各种各样的事情,比如获取它的子节点(通过 MSXML 接口,而不是通过我的包装器)或其名称等。我知道我的包装器 可以 工作,因为我使用它在应用程序的其他地方用于解析 XML 配置文件,并且可以完美运行。

我想也许我在使用 XPath 查询表达式时做错了什么,但我能想到的每一种排列都不会让人高兴。当我尝试处理这个 XML 文件时,IXMLDOMNodePtr::SelectNodes() 返回的 MSXML::IXMLDOMNodeListPtr 的长度始终为 0。

这快把我逼疯了。

【问题讨论】:

  • 我会注意到,如果我用我的配置文件之一替换此处的 XML 文档并尝试选择它的节点,它会完美运行。此 XML 文件是否存在格式错误?

标签: c++ xpath msxml selectnodes


【解决方案1】:

我习惯用 .NET 的 XmlDocument 对象来做这件事,但我觉得这里的效果是一样的:

如果 XML 文档包含一个名称空间——甚至是一个未命名的名称空间——那么 Xpath 查询也必须使用一个名称空间。因此,您必须将命名空间添加到 XMLDoument 中,您不妨在代码中给出一个名称,并在 XPATH 查询中包含前缀(xml 文档和xpath,只要命名空间整理出来)

所以,当您使用像 /report/subreport/record/field/value 这样的 XPath 时,您实际上需要首先设置文档的命名空间:

  pXMLDoc->setProperty(_bstr_t("SelectionNamespaces"),
                       _bstr_t("xmlns:r="http://www.**.com/**"));

然后selectNodes() 使用/r:report/r:subreport/r:record/r:field/r:value

【讨论】:

  • James,我尝试了这个方法(在将我的 XMLDoc 指针更改为 IXMLDOMDocument2Ptr 之后),但是在尝试调用这个 setProperty 方法时出现了 COM 错误。
  • 哎呀,我传入的命名空间声明缺少一些引号。解决了这个问题,一切正常。谢谢!
【解决方案2】:

当您选择节点时,我没有看到对命名空间的引用。我希望这是根本问题。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2021-11-14
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多