Я работаю над приложением для автоматического тестирования, и в настоящее время я пишу функцию, которая сравнивает значения двух 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-документа. (CRecord является абстрактным классом с CXMLRecord одним из его подклассов, который предоставляет доступ к дочерним записям по отдельности или по группам, а также разрешает доступ к «значению» записи (значения для дочерних элементов или атрибутов в случае 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, но каждая перестановка, о которой я мог подумать, не приносит радости. MSXML::IXMLDOMNodeListPtr, возвращаемый IXMLDOMNodePtr::SelectNodes(), всегда имеет длину 0, когда я пытаюсь обработать этот XML-файл.
Это сводит меня с ума.





Я не вижу ссылки на пространство имен, когда вы выбираете узлы. Я ожидал, что это основная проблема.
Я привык делать это с объектами .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
Джеймс, я попробовал этот метод (после изменения указателя XMLDoc на IXMLDOMDocument2Ptr), но при попытке вызвать этот метод setProperty я получаю сообщение об ошибке COM.
Упс, в моем объявлении пространства имен отсутствовали кавычки. Исправлено, и все работает нормально. Спасибо!
Замечу, что если я заменю здесь XML-документ одним из моих файлов конфигурации и попытаюсь выбрать его узлы, он будет работать отлично. Есть ли что-то искаженное в этом XML-файле?