Я пытаюсь получить имя тега дочернего узла в документе XML. Мой XML-документ выглядит примерно так:
<?xml version="1.0" encoding="utf-8"?>
<Parent>
<child1>
<grandchild1>someinfo1</grandchild1>
<grandchild2>someinfo2</grandchild2>
</child1>
<child2>
<grandchild3>someinfo3</grandchild3>
<grandchild4>someinfo4</grandchild4>
</child2>
</Parent>
Мне нужно зациклить и найти имена тегов, такие как child1 grandchild1 и т. Д.
Мой код для выполнения следующих действий:
IXMLDOMDocument *pXMLDom = NULL;
IXMLDOMNodeList *pNodes = NULL;
IXMLDOMNode *pNode = NULL;
pXMLDom->put_async(VARIANT_FALSE);
pXMLDom->put_validateOnParse(VARIANT_TRUE);
pXMLDom->put_resolveExternals(VARIANT_FALSE);
pXMLDom->put_preserveWhiteSpace(VARIANT_TRUE);
BSTR parentNode = SysAllocString(L"//Parent/*");
pXMLDom->selectNodes(parentNode, &pNodes);
pNodes->get_length(&length);
for (int i = 0; i < length; i++)
{
pNodes->get_item(i, &pNode);
BSTR temp = NULL;
pNode->get_xml(&temp);
printf("Node (%d), <%S>:\n", i, temp); // works fine until this point
IXMLDOMNode *firstChild;
pNode->get_firstChild(&firstChild);
IXMLDOMNodeList *childNodes;
pNode->get_childNodes(&childNodes);
firstChild->get_nodeName(&temp); // Does not work
firstChild->get_baseName(&temp); // Does not work
}
Обратите внимание, что я предоставил только очень минималистскую версию своего кода для простоты. Если понадобятся какие-либо дополнительные разъяснения или код, я буду рад предоставить. Любые указатели в правильном направлении будут полезны. Большая часть кода была написана с помощью msdn.
XML состоит из узлов, и существует много разных видов узлов (элементы, атрибуты, текст, пространства имен, инструкции по обработке, комментарии, документы и т. Д.).
Узел элемента XML, содержащий текстовое содержимое, будет иметь дочерний узел с именем #text
, Это продиктовано спецификацией XML. Итак, в вашем примере, grandchild1
, grandchild2
, grandchild3
, а также grandchild4
у всех есть ребенок #text
узел, например:
Документ | | _ PI: <? xml version = "1.0" encoding = "utf-8"?> | | _ Элемент: "Родитель" | | _ Элемент: "child1" | | | | _ Элемент: "grandchild1" | | | | | | _ #text "someinfo1" | | | | _ Элемент: "grandchild2" | | | | _ #text "someinfo2" | | _ Элемент: "child2" | | _ Элемент: "grandchild3" | | | | _ #text: "someinfo3" | | _ Элемент: "grandchild4" | | _ #text: "someinfo4"
Даже пробелы между элементами, даже если это просто разрывы строк, сохраняются как дополнительные текстовые узлы (потому что вы устанавливаете preserveWhiteSpace
вариант true), например:
Документ | | _ PI: <? xml version = "1.0" encoding = "utf-8"?> | | _ #text "\ r \ n" | | _ Элемент: "Родитель" | | _ #text "\ r \ n" | | _ Элемент: "child1" | | | | _ #text "\ r \ n" | | | | _ Элемент: "grandchild1" | | | | | | _ #text "someinfo1" | | | | _ #text "\ r \ n" | | | | _ Элемент: "grandchild2" | | | | _ #text "someinfo2" | | _ #text "\ r \ n" | | _ Элемент: "child2" | | | | _ #text "\ r \ n" | | | | _ Элемент: "grandchild3" | | | | | | _ #text: "someinfo3" | | | | _ #text "\ r \ n" | | | | _ Элемент: "grandchild4" | | | | | | _ #text: "someinfo4" | | | | _ #text "\ r \ n" | | _ #text "\ r \ n"
XPath ищет все узлы, но *
Подстановочный знак соответствует только узлам элемента. Но вы вручную сверляете дочерние элементы найденных элементов, поэтому вы столкнетесь с #text
узлы. Для того, что вы пытаетесь сделать, отключите сохранение пробелов, чтобы удалить ненужные текстовые узлы пробелов, а затем сосредоточьтесь только на дочерних узлах элемента, например:
IXMLDOMDocument *pXMLDom = NULL;
IXMLDOMNodeList *pNodes = NULL;
IXMLDOMNode *pNode = NULL;
long length = 0;
// create pXMLDom as needed ...
pXMLDom->put_async(VARIANT_FALSE);
pXMLDom->put_validateOnParse(VARIANT_TRUE);
pXMLDom->put_resolveExternals(VARIANT_FALSE);
pXMLDom->put_preserveWhiteSpace(VARIANT_FALSE); // <--
BSTR parentNode = SysAllocString(L"//Parent/*");
HRESULT hRes = pXMLDom->selectNodes(parentNode, &pNodes);
SysFreeString(parentNode);
if (SUCCEEDED(hRes))
{
pNodes->get_length(&length);
for (int i = 0; i < length; ++i)
{
hRes = pNodes->get_item(i, &pNode);
if (SUCCEEDED(hRes))
{
BSTR name = NULL;
hRes = pNode->get_nodeName(&name);
if (SUCCEEDED(hRes))
{
printf("Node (%d), <%S>:\n", i, name);
SysFreeString(name);
}
IXMLDOMNode *pChild = NULL;
hRes = pNode->get_firstChild(&pChild);
if (hRes == S_OK)
{
do
{
DOMNodeType type;
hRes = pChild->get_nodeType(&type);
if ((SUCCEEDED(hRes) && (type == NODE_ELEMENT))
{
hRes = pNode->get_nodeName(&name);
if (SUCCEEDED(hRes))
{
printf(" %S\n", name);
SysFreeString(name);
}
}
IXMLDOMNode *pSibling = NULL;
hRes = pChild->get_nextSibling(&pSibling);
if (hRes != S_OK) break;
pChild->Release();
pChild = pSibling;
}
while (true);
pChild->Release();
}
pNode->Release();
}
}
pNodes->Release();
}
...
pXMLDom->Release();
Если вам нужно пройти более 2 уровней, вам следует вместо этого установить рекурсивный цикл, например:
void processNode(IXMLDOMNode *pNode)
{
BSTR name = NULL;
hRes = pNode->get_nodeName(&name);
if (SUCCEEDED(hRes))
{
printf("%S\n", name);
SysFreeString(name);
}
IXMLDOMNode *pChild = NULL;
hRes = pNode->get_firstChild(&pChild);
if (hRes == S_OK)
{
do
{
DOMNodeType type;
hRes = pChild->get_nodeType(&type);
if ((SUCCEEDED(hRes) && (type == NODE_ELEMENT))
processNode(pChild);
IXMLDOMNode *pSibling = NULL;
hRes = pChild->get_nextSibling(&pSibling);
if (hRes != S_OK) break;
pChild->Release();
pChild = pSibling;
}
while (true);
pChild->Release();
}
}
...
IXMLDOMDocument *pXMLDom = NULL;
IXMLDOMNodeList *pNodes = NULL;
IXMLDOMNode *pNode = NULL;
long length = 0;
// create pXMLDom as needed ...
pXMLDom->put_async(VARIANT_FALSE);
pXMLDom->put_validateOnParse(VARIANT_TRUE);
pXMLDom->put_resolveExternals(VARIANT_FALSE);
pXMLDom->put_preserveWhiteSpace(VARIANT_FALSE); // <--
BSTR parentNode = SysAllocString(L"//Parent/*");
HRESULT hRes = pXMLDom->selectNodes(parentNode, &pNodes);
SysFreeString(parentNode);
if (SUCCEEDED(hRes))
{
pNodes->get_length(&length);
for (int i = 0; i < length; ++i)
{
hRes = pNodes->get_item(i, &pNode);
if (SUCCEEDED(hRes))
{
processNode(pNode);
pNode->Release();
}
}
pNodes->Release();
}
...
pXMLDom->Release();
После того, как я разместил вопрос, я получил то, что искал!
Несохраняющие пробелы работы:
pXMLDom->put_preserveWhiteSpace(VARIANT_FALSE);