MSXML选择节点不起作用

时间:2008-11-12 17:42:38

标签: c++ xpath msxml selectnodes

我正在开发一个自动化测试应用程序,目前我正在编写一个函数来比较两个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>

(请注意,可能有多个<subreport>元素,其中包含多个<record>元素。)

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

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

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

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

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

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

我想也许我正在做一些有关XPath查询表达式的错误,但我能想到的每一个排列都没有带来快乐。当我尝试处理此XML文件时,MSXML::IXMLDOMNodeListPtr返回的IXMLDOMNodePtr::SelectNodes()的长度始终为0。

这让我发疯了。

2 个答案:

答案 0 :(得分:6)

我习惯用.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

答案 1 :(得分:0)

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

相关问题