Демонстрационное приложение, использующее XML DOM
Создать навороченное приложение, использующее множество различных возможностей MSXML, совсем не сложно, но лишний код может только добавить путаницы. Поэтому я решил написать простое консольное приложение, которое выполняет четыре основных операции:
• Загружает XML-файл с диска.
• Отыскивает определённый узел и добавляет к нему дочерний узел.
• Находит ещё один узел и отображает содержащийся в нём текст.
• Сохраняет изменённый документ на диск.
Чтобы ещё больше упростить задачу, я жёстко "зашил" в программу имена XML-файлов и узлов. Понятно, что в реальном приложении вы вряд ли примените эту тактику. Но в нашем случае она имеет смысл, так как ещё больше упрощает код, связанный с использованием MSXML.
Как и во многих других случаях, я использовал в своём примере библиотеку ATL как удобную обёртку для всех операций, связанных с COM. Поэтому вы непременно увидете, как я использую объекты CComPtr и CComQIPtr. Для ровного счёта я добавил к ним также объекты CComBSTR и CComVariant. Если они вам не знакомы, просто запомните, что они являются шаблонами и сами заботятся о многих деталях, которые для наших целей несущественны. Для нас важно рассмотреть, каким образом искать узлы XML, добавлять новые узлы и отображать содержащийся в них текст.
Моё консольное приложение будет загружать XML-документ под названием xmldata.xml (предполагается, что он лежит в одном каталоге с исполняемым файлом), содержащий следующие данные:
Hello, World!
Сначала мы будем искать узел xmlnode, и если найдём, добавим к нему новый узел (с атрибутом) в качестве дочернего. В результате получится документ следующего вида:
Hello, World!
Далее мы напечатаем сообщение, содержащееся в узле xmltext ("Hello, World!"), и сохраним полученный документ в файл updatedxml.xml. После этого вы сможете посмотреть результаты, используя текстовый редактор или Internet Explorer 5.x. Давайте займёмся кодом.
Прежде всего приложение инициализирует библиотеку COM, а затем создаёт экземпляр парсера MSXML:
CComPtr spXMLDOM;
HRESULT hr = spXMLDOM.CoCreateInstance(uuidof(DOMDocument));
if (FAILED(hr)) throw "Unable to create XML parser object";
if (spXMLDOM.p == NULL) throw "Unable to create XML parser object";
Если нам удалось создать экземпляр парсера, мы загружаем в него XML-документ:
VARIANT_BOOL bSuccess = false;
hr = spXMLDOM->load(CComVariant(L"xmldata.xml"), &bSuccess);
if (FAILED(hr)) throw "Unable to load XML document into the parser";
if (!bSuccess) throw "Unable to load XML document into the parser";
Поиск узла осуществляется через объект документа, поэтому мы используем IXMLDOMDocument::selectSingleNode() для обнаружения нужного узла по его имени. Есть и другие способы, но этот наиболее прост, в том случае, если вы точно знаете, какой узел вам требуется.
CComBSTR bstrSS(L"xmldata/xmlnode");
CComPtr spXMLNode;
hr = spXMLDOM->selectSingleNode(bstrSS, &spXMLNode);
if (FAILED(hr)) throw "Unable to locate 'xmlnode' XML node";
if (spXMLNode.p == NULL) throw "Unable to locate 'xmlnode' XML node";
Другие методы, о которых вам следует знать, – это IXMLDOMDocument::nodeFromID() и IXMLDOMElement::getElementsByTagName(), которые вы можете использовать, чтобы получить список узлов в документе. Вы также можете обратиться к документу как к дереву и просканировать его (получая дочерний узел, все узлы одного уровня и т. д.).
В любом случае, результатом поиска станет объект узла MSXML, IXMLDOMNode. Узел должен существовать где-то в документе, иначе поиск закончится неудачей. Моё приложение использует его как родителя для совершенно нового узла, который создаётся объектом XML-документа:
CComPtr spXMLChildNode;
hr = spXMLDOM->createNode(CComVariant(NODE_ELEMENT), CComBSTR("xmlchildnode"), NULL, &spXMLChildNode);
if (FAILED(hr)) throw "Unable to create 'xmlchildnode' XML node";
if (spXMLChildNode.p == NULL) throw "Unable to create 'xmlchildnode' XML node";
Если парсеру удалось создать новый узел, следующий шаг – разместить его в дереве XML. Метод IXMLDOMNode::appendChild() – как раз то, что нам нужно.
CComPtr spInsertedNode;
hr = spXMLNode->appendChild(spXMLChildNode, &spInsertedNode);
if (FAILED(hr)) throw "Unable to move 'xmlchildnode' XML node";
if (spInsertedNode.p == NULL) throw "Unable to move 'xmlchildnode' XML node";
Если родительский узел принял только что созданный узел в качестве дочернего, он вернёт вам ещё один экземпляр IXMLDOMNode, который представляет новый узел. На самом деле, этот новый узел и узел, который вы передали в appendChild(), в точности совпадают. Тем не менее, проверка указателя на добавленный дочерний узел может быть полезной, так как в случае ошибки он примет значение NULL.
Итак, мы уже нашли требуемый узел и добавили к нему дочерний узел; теперь посмотрим, как работать с атрибутами. Представьте себе, что вам нужно добавить к новому дочернему узлу атрибут:
Читать дальше