我正在开发一个自动化测试应用程序,目前我正在编写一个函数来比较两个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。
这让我发疯了。
答案 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)
当您选择节点时,我看不到对命名空间的引用。我希望这是根本问题。