我正在开发一个自动化测试应用程序,目前我正在编写一个函数来比较两个XML文件之间的值,这些文件应该是相同的,但可能不是.以下是我正在尝试处理的XML示例:
0 1.000000 0.000000 N/A
(注意,可能有多个
元素,其中包含多个
元素.)
我想要的是提取
两个文档的标签,然后比较它们的值.那部分我知道怎么做.问题在于提取本身.
由于我遇到了C++,我正在使用MSXML,并编写了一个包装器,允许我的应用程序抽象出实际的XML操作,以防我决定更改我的数据格式.
该包装器CSimpleXMLParser加载XML文档并将其"最高记录"设置为XML文档的document元素.(CRecord是一个抽象类,其CXMLRecord是其子类之一,它可以单独或按组访问子记录,还允许访问Record的"值"(对于子元素或属性的值,在CXMLRecord的情况下) .)CXMLRecord包含一个MSXML :: MSXMLDOMNodePtr和一个指向CSimpleXMLParser实例的指针.)包装器还包含返回子项的实用程序函数,CXMLRecord用它来返回子记录.
在我的代码中,我执行以下操作(尝试返回所有
节点只是为了查看它是否有效):
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)); }
运行时,顶级记录肯定
正确地设置为元素.我可以用它做各种各样的事情,比如获取它的子节点(通过MSXML接口,而不是通过我的包装器)或它的名称等.我知道我的包装器可以工作,因为我在应用程序的其他地方使用它来解析一个XML配置文件,它完美无瑕.
我想也许我正在做一些错误的XPath查询表达式,但我能想到的每一个排列都没有带来任何乐趣.当我尝试处理这个XML文件时,MSXML::IXMLDOMNodeListPtr
返回的IXMLDOMNodePtr::SelectNodes()
值总是为0.
这真让我抓狂.
我习惯用.NET的XmlDocument对象做这个,但我认为这里的效果是一样的:
如果XML文档包含命名空间 - 甚至是未命名的命名空间 - 那么Xpath查询也必须使用一个命名空间.因此,您必须将命名空间添加到XMLDoument中,您也可以在代码中给出一个名称,并在XPATH查询中包含前缀(xml文档和前缀之间的前缀不同并不重要) xpath,只要名称空间对其进行排序)
因此,当您使用XPath时/report/subreport/record/field/value
,您实际上需要首先设置文档的命名空间:
pXMLDoc->setProperty(_bstr_t("SelectionNamespaces"), _bstr_t("xmlns:r="http://www.**.com/**"));
然后selectNodes()
使用 /r:report/r:subreport/r:record/r:field/r:value