MSXML 选择节点不工作
我正在开发一个自动化测试应用程序,目前正在编写一个函数来比较两个 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>
请注意,可能有多个
元素,并且其中可能有多个
元素。 )
我想要的是提取两个文档的
标签,然后比较它们的值。 那部分我知道该怎么做。 问题在于提取本身。
由于我陷入了 C++ 困境,所以我使用 MSXML,并编写了一个包装器,以允许我的应用程序抽象出实际的 XML 操作,以防我决定更改数据格式。
该包装器 CSimpleXMLParser 加载 XML 文档并将其“顶部记录”设置为 XML 文档的文档元素。 (CRecord 是一个抽象类,CXMLRecord 是其子类之一,它可以单独或按组访问子记录,还允许访问记录的“值”(在 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 文件时,IXMLDOMNodePtr::SelectNodes()
返回的 MSXML::IXMLDOMNodeListPtr
的长度始终为 0。
这真让我抓狂。
I am working on an automated testing app, and am currently in the process of writing a function that compares values between two XML files that should be identical, but may not be. Here is a sample of the XML I'm trying to process:
<?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>
(Note there may be multiple <subreport>
elements and within those, multiple <record>
elements.)
What I'd like is to extract the <value>
tags of two documents and then compare their values. That part I know how to do. The problem is the extraction itself.
Since I'm stuck in C++, I'm using MSXML, and have written a wrapper to allow my app to abstract away the actual XML manipulation, in case I ever decide to change my data format.
That wrapper, CSimpleXMLParser, loads an XML document and sets its "top record" to the document element of the XML document. (CRecord being an abstract class with CXMLRecord one of its subclasses, and which gives access to child records singularly or by group, and also allowing access to the "value" of the Record (values for child elements or attributes, in the case of CXMLRecord.) A CXMLRecord contains an MSXML::MSXMLDOMNodePtr and a pointer to an instance of a CSimpleXMLParser.) The wrapper also contains utility functions for returning children, which the CXMLRecord uses to return its child records.
In my code, I do the following (trying to return all <subreport>
nodes just to see if it works):
CSimpleXMLParser parserReportData;
parserReportData.OpenXMLDocument(strPathToXML);
bool bGetChildrenSuccess = parserReportData.GetFirstRecord()->GetChildRecords(listpChildren, _T("subreport"));
This is always returning false. The meat of the implementation of CXMLRecord::GetChildRecords() is basically
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;
And CSimpleXMLParser::SelectNodes() is:
MSXML2::IXMLDOMNodeListPtr CSimpleXMLParser::SelectNodes(LPCTSTR strXPathFilter, MSXML2::IXMLDOMNodePtr pXMLNode)
{
return pXMLNode->selectNodes(_bstr_t(strXPathFilter));
}
When run, the top record is definitely being set to the <report>
element properly. I can do all sorts of things with it, like getting its child nodes (through the MSXML interface, not through my wrapper) or its name, etc. I know that my wrapper can work, because I use it elsewhere in the app for parsing an XML configuration file, and that works flawlessly.
I thought maybe I was doing something faulty with the XPath query expression, but every permutation I could think of gives no joy. The MSXML::IXMLDOMNodeListPtr
returned by IXMLDOMNodePtr::SelectNodes()
is always of length 0 when I try to deal with this XML file.
This is driving me crazy.
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(2)
我习惯于使用 .NET 的 XmlDocument 对象执行此操作,但我认为这里的效果是相同的:
如果 XML 文档包含命名空间(即使是未命名的命名空间),那么 Xpath 查询也必须使用命名空间。 因此,您必须将命名空间添加到 XMLDoument,您也可以在代码中为其指定名称,并在 XPATH 查询中包含前缀(XML 文档和 XML 文档之间的前缀不同并不重要) xpath,只要命名空间将其排序)
所以,当您使用像
/report/subreport/record/field/value
这样的 XPath 时,您实际上需要首先设置文档的命名空间:然后使用
/r:report/r:subreport/r:record/r:field/r:value
selectNodes()
I'm used to doing this with .NET's XmlDocument objects, but I think the effect is the same here:
If the XML document includes a namespace -- even an unnamed one -- then the Xpath query has to use one as well. So, you'll have to add the namespace to the XMLDoument which you might as well give a name in the code, and the include the prefix in the XPATH query (it doesn't matter that the prefixes are different between the xml document and the xpath, as long as the namespaces sort it out)
SO, while you are using an XPath like
/report/subreport/record/field/value
, you actually need to first set the namespace of your document:and then
selectNodes()
using/r:report/r:subreport/r:record/r:field/r:value
当您选择节点时,我没有看到任何对命名空间的引用。 我认为这是根本问题。
I see no reference to a namespace when you're selecting nodes. I'd expect this to be the fundamental problem.