写Unmanaged Code在.NET时代成为一种很悲惨的事,当你需要处理XML文件时,这种感觉会变得尤其强烈。FCL中的System.Xml多简单啊,连Steve Ballmer都知道怎么用。
事情不会总是那么理想的,如果你要在C/C++程序里处理XML怎么办呢?
选择一:市面上的XML lib还是有几个的,最有名的当然是libxml。我一年前用过,很不错,我还特意写了一份简明教程,后来不知搁哪儿了。
二法
C++操作XML编程实例
2008-06-19 21:43:07 阅读31 评论0 字号:大中小
#include <iostream>
#import <msxml.dll> //引入类型库
using namespace std;
int Exit();
void LoadFromString();
void CreateXML();
//XML文本模板
_bstr_t XMLTemple="<China><Beijing>-11</Beijing><Shanghai Weather=/"Cloudy/">9</Shanghai></China>";
int main()
{
cout << " XML编程--示范程序 " << endl;
CoInitialize(NULL); //初始化COM 环境
cout << "------生成新的XML文本 ------" << endl;
CreateXML();
cout << "------读取已有XML文本 ------" << endl;
LoadFromString();
return Exit();
}
void CreateXML()
{
MSXML::IXMLDOMDocumentPtr pDoc;
HRESULT hr =pDoc.CreateInstance(__uuidof(MSXML::DOMDocument));
if(!SUCCEEDED(hr))
{
cout << "无法创建DOMDocument对象,请检查是否安装了MS XML Parser 运行库!" << endl;
Exit();
}
MSXML::IXMLDOMElementPtr pDocElement=pDoc->createElement("China");
pDoc->appendChild(pDocElement);
cout << "生成树根:/n" << pDoc->xml << endl;
MSXML::IXMLDOMElementPtr pNewChildElement;
pNewChildElement=pDoc->createElement("Beijing");
pNewChildElement->Puttext("-11");
pDocElement->appendChild(pNewChildElement);
cout << "添加节点:/n" << pDoc->xml << endl;
pNewChildElement=pDoc->createElement("Shanghai");
pNewChildElement->Puttext("9");
pNewChildElement->setAttribute("Weather",_variant_t("Cloudy"));
pDocElement->appendChild(pNewChildElement);
cout << "再添加节点:/n" << pDoc->xml << endl;
pDocElement->removeChild (pNewChildElement);
cout << "把刚加入的节点删除:/n" << pDoc->xml << endl;
}
void LoadFromString()
{
MSXML::IXMLDOMDocumentPtr pDoc;
HRESULT hr =pDoc.CreateInstance(__uuidof(MSXML::DOMDocument));
if(!SUCCEEDED(hr))
{
cout << "无法创建DOMDocument对象,请检查是否安装了MS XML Parser 运行库!" << endl;
Exit();
}
pDoc->loadXML(XMLTemple);
cout << "读取结果:/n" << pDoc->xml << endl;
MSXML::IXMLDOMElementPtr pDocElement=pDoc->GetdocumentElement();
MSXML::IXMLDOMElementPtr pElement=pDocElement->selectSingleNode("Shanghai");
pDocElement->removeChild(pElement);
cout << "定位删除Shanghai节点:/n" << pDoc->xml << endl;
cout << "保存结果 Save .....(模拟而已)" << endl;
cout << "/n 好了,就这么简单" << endl;
}
int Exit()
{
getchar();
return 1;
}
三法
C++标准库中没有操作XML的方法,用C++操作XML文件必须熟悉一种函数库,选用LIBXML2
Libxml2是一个C语言的XML程序库,可以简单方便的提供对XML文档的各种操作,并且支持XPATH查询,以及部分的支持XSLT转换等功能。
Libxml2的下载地址是
windows版本的的下载地址是
libxml2库依赖iconv和zlib库,所以需要下载三个
成功版本libxml2-2.6.30.win32.zip、zlib-1.2.3.win32.zip和iconv-1.9.2.win32.zip。
解压,在系统变量path中加上 iconv-1.9.2.win32/bin;zlib-1.2.3.win32/bin;libxml2-2.6.30.win32/bin这三个地址。或者把其中的三个dll到拷贝到system32目录中
编译链接基于libxml2的程序,在VC环境中设置lib和include路径,并在link设置中添加libxml2.lib和iconv.lib.
vc:项目->属性->c/c++->常规->附加包含目录,将三个文件夹的include下的.h头文件包含进工程
项目->属性->链接器->常规->附加库目录,将三个文件夹的bin下的.lib库文件包含进工程
注意,这只是将目录包含进工程,需要使用时需在代码中写
#include <libxml/parser.h>(eg)
和
#pragma comment(lib,"libxml2.lib")(eg)
实验代码如下
/********************************************************************
created: 2007/11/09
created: 9:11:2007 15:34
filename: CreateXmlFile.cpp
author: Wang xuebin
depend: libxml2.lib
build: nmake TARGET_NAME=CreateXmlFile
purpose: 创建一个xml文件
*********************************************************************/
#pragma comment(lib,"libxml2.lib") #pragma comment(lib,"iconv.lib")
#include <stdio.h>
#include <libxml/parser.h>
#include <libxml/tree.h>
#include <iostream> using namespace std;
int main()
{
//定义文档和节点指针
xmlDocPtr doc = xmlNewDoc(BAD_CAST"1.0");
xmlNodePtr root_node = xmlNewNode(NULL,BAD_CAST"root");
//设置根节点
xmlDocSetRootElement(doc,root_node);
//在根节点中直接创建节点
xmlNewTextChild(root_node, NULL, BAD_CAST "newNode1", BAD_CAST "newNode1 content");
xmlNewTextChild(root_node, NULL, BAD_CAST "newNode2", BAD_CAST "newNode2 content");
xmlNewTextChild(root_node, NULL, BAD_CAST "newNode3", BAD_CAST "newNode3 content");
//创建一个节点,设置其内容和属性,然后加入根结点
xmlNodePtr node = xmlNewNode(NULL,BAD_CAST"node2");
xmlNodePtr content = xmlNewText(BAD_CAST"NODE CONTENT");
xmlAddChild(root_node,node);
xmlAddChild(node,content);
xmlNewProp(node,BAD_CAST"attribute",BAD_CAST "yes");
//创建一个儿子和孙子节点
node = xmlNewNode(NULL, BAD_CAST "son");
xmlAddChild(root_node,node);
xmlNodePtr grandson = xmlNewNode(NULL, BAD_CAST "grandson");
xmlAddChild(node,grandson);
xmlAddChild(grandson, xmlNewText(BAD_CAST "This is a grandson node"));
//存储xml文档
int nRel = xmlSaveFile("CreatedXml.xml",doc);
if (nRel != -1)
{
cout<<"一个xml文档被创建,写入"<<nRel<<"个字节"<<endl;
}
//释放文档内节点动态申请的内存
xmlFreeDoc(doc);
return 1;
}
创建一个xml文档其流程如下:
l 用xmlNewDoc函数创建一个文档指针doc;
l 用xmlNewNode函数创建一个节点指针root_node;
l 用xmlDocSetRootElement将root_node设置为doc的根结点;
l 给root_node添加一系列的子节点,并设置子节点的内容和属性;
l 用xmlSaveFile将xml文档存入文件;
l 用xmlFreeDoc函数关闭文档指针,并清除本文档中所有节点动态申请的内存。
注意,有多种方式可以添加子节点:第一是用xmlNewTextChild直接添加一个文本子节点;第二是先创建新节点,然后用xmlAddChild将新节点加入上层节点。
产生如下文件
法四
——需要的包
#include <XMLDoc.hpp> ——相关说明 _di_IXMLDocument 为模板类 typedef System::DelphiInterface< IXMLDocument > _di_IXMLDocument; _di_IXMLNode typedef System::DelphiInterface< IXMLNode > _di_IXMLNode; _di_IXMLNodeList 同 ——类方法 //设置参数 void TXXX::setOptions(String name,String value){ //创建文档对象 _di_IXMLDocument XMLDoc = LoadXMLDocument(L"文件路径"); XMLDoc->Active=true; //文档根节点 _di_IXMLNode root = XMLDoc->DocumentElement; //想要查找节点 _di_IXMLNode tempNode; //调用搜索方法 searchXml(name,root,tempNode); // 处理 if(tempNode!=NULL) tempNode->SetText(value); XMLDoc->SaveToFile(L"文件路径"); } //递归搜索参数节点树 void TXXX::searchXml(String name,_di_IXMLNode &Parent_Node,_di_IXMLNode& tempNode){ _di_IXMLNode Child_Node; //子结点 //子节点列表 _di_IXMLNodeList list = Parent_Node->ChildNodes; for(int i=0;i<list->Count;i++) { Child_Node = list->Get(i); //递归结束条件 if(Child_Node->GetNodeName()==name) { tempNode = Child_Node; break; } else { //递归函数 searchXml(name,Child_Node,tempNode); } } }
法五
写本文的目的是为了方便大家了解C++ MSXML操作方法。
当然,C++中对MSXML的调用有多种,本文采用的方法是完全参照MSXML SDK提供的文档进行操作。
如果有什么错误,欢迎指正。
代码框架是基于vs2008 MFC 对话框程序(UNICODE)。对话框程序需要读者自己创建。
view plaincopy to clipboardprint?
#include <msxml6.h> #include <comutil.h> #pragma comment(lib, "comsuppwd.lib") void CXmlSampleDlg::OnBnClickedButton1()//按钮事件 { CoInitialize(NULL); CComPtr<IXMLDOMDocument> spXmldoc; HRESULT hr = spXmldoc.CoCreateInstance(L"MSXML2.DOMDocument.6.0"); if(SUCCEEDED(hr)) { VARIANT_BOOL isSuccessFul; CComVariant varXmlFile(L"a.xml"); spXmldoc->put_async(VARIANT_FALSE); HRESULT hr= spXmldoc->load(varXmlFile, &isSuccessFul); if(isSuccessFul==VARIANT_TRUE) { CComBSTR bstrXml; CComPtr<IXMLDOMElement> spRoot=NULL; CComPtr<IXMLDOMElement> spTheBook=NULL; CComPtr<IXMLDOMElement> spTheElem=NULL; CComPtr<IXMLDOMNode> spNewNode=NULL; hr = spXmldoc->get_documentElement(&spRoot); spRoot->get_xml(&bstrXml); AfxMessageBox(L"1, 原始的XML"); AfxMessageBox(bstrXml); spXmldoc->createElement(L"book", &spTheBook); spXmldoc->createElement(L"name", &spTheElem); spXmldoc->put_text(L"新书"); spTheBook->appendChild(spTheElem, &spNewNode); spTheElem.Release(); spNewNode.Release(); spXmldoc->createElement(L"price", &spTheElem); spTheElem->put_text(L"20"); spTheBook->appendChild(spTheElem, &spNewNode); spTheElem.Release(); spNewNode.Release(); spXmldoc->createElement(L"memo", &spTheElem); spTheElem->put_text(L"新书的更好看。"); spTheBook->appendChild(spTheElem, &spNewNode); spNewNode.Release(); spTheElem.Release(); spRoot->appendChild(spTheBook, &spNewNode); spNewNode.Release(); spTheBook.Release(); spRoot->get_xml(&bstrXml); AfxMessageBox(L"2, 新建一本书完成"); AfxMessageBox(bstrXml); --- 新建一本书完成 ---- --- 下面对《哈里波特》做一些修改。 ---- --- 查询找《哈里波特》---- CComPtr<IXMLDOMNode> spTheNode=NULL; spRoot->selectSingleNode(L"/books/book[name='哈里波特']", &spTheNode); hr=spTheNode.QueryInterface(&spTheBook); spTheNode.Release(); spTheBook->get_xml(&bstrXml); AfxMessageBox(L"3,《哈里波特》的XML"); AfxMessageBox(bstrXml); --- 此时修改这本书的价格 ----- CComPtr<IXMLDOMNodeList> spNodeList=NULL; CComPtr<IXMLDOMNode> spListItem=NULL; spTheBook->get_childNodes(&spNodeList); spNodeList->get_item(1, &spListItem); spNodeList.Release(); spListItem->put_text(L"15"); --- 另外还想加一个属性id,值为B01 ---- CComVariant varId(L"B01"); spTheBook->setAttribute(L"id", varId); varId.Clear(); spTheBook->get_xml(&bstrXml); spTheBook.Release(); AfxMessageBox(L"4, 对《哈里波特》修改完成。"); AfxMessageBox(bstrXml); --- 对《哈里波特》修改完成。 ---- --- 要用id属性删除《三国演义》这本书 ---- spRoot->selectSingleNode(L"/books/book[@id='B02']", &spTheNode); hr=spTheNode.QueryInterface(&spTheBook); spTheNode.Release(); spTheBook->get_xml(&bstrXml); AfxMessageBox(L"5, 《三国演义》的XML"); AfxMessageBox(bstrXml); CComPtr<IXMLDOMNode> spParentNode=NULL; spTheBook->get_parentNode(&spParentNode); spParentNode->removeChild(spTheBook, &spTheNode); spTheNode.Release(); spParentNode.Release(); spTheBook.Release(); spRoot->get_xml(&bstrXml); AfxMessageBox(L"6, 删除《三国演义》后的XML"); AfxMessageBox(bstrXml); --- 再将所有价格低于10的书删除 ---- spRoot->selectNodes(L"/books/book[price<10]", &spNodeList); CComQIPtr<IXMLDOMSelection> spSomeBooks=spNodeList; spNodeList.Release(); spSomeBooks->removeAll(); spSomeBooks.Release(); spXmldoc->get_xml(&bstrXml); AfxMessageBox(L"7, 已经删除价格低于10的书"); AfxMessageBox(bstrXml); spRoot.Release(); bstrXml.Empty(); //spXmldoc->save(varXmlFile); //保存xml。 } varXmlFile.ClearToZero(); } spXmldoc.Release(); CoUninitialize(); } #include <msxml6.h> #include <comutil.h> #pragma comment(lib, "comsuppwd.lib") void CXmlSampleDlg::OnBnClickedButton1()//按钮事件 { CoInitialize(NULL); CComPtr<IXMLDOMDocument> spXmldoc; HRESULT hr = spXmldoc.CoCreateInstance(L"MSXML2.DOMDocument.6.0");if(SUCCEEDED(hr))
{ VARIANT_BOOL isSuccessFul; CComVariant varXmlFile(L"a.xml");spXmldoc->put_async(VARIANT_FALSE);
HRESULT hr= spXmldoc->load(varXmlFile, &isSuccessFul);if(isSuccessFul==VARIANT_TRUE)
{ CComBSTR bstrXml; CComPtr<IXMLDOMElement> spRoot=NULL; CComPtr<IXMLDOMElement> spTheBook=NULL; CComPtr<IXMLDOMElement> spTheElem=NULL; CComPtr<IXMLDOMNode> spNewNode=NULL;hr = spXmldoc->get_documentElement(&spRoot);
spRoot->get_xml(&bstrXml); AfxMessageBox(L"1, 原始的XML"); AfxMessageBox(bstrXml); spXmldoc->createElement(L"book", &spTheBook); spXmldoc->createElement(L"name", &spTheElem); spXmldoc->put_text(L"新书"); spTheBook->appendChild(spTheElem, &spNewNode); spTheElem.Release(); spNewNode.Release();spXmldoc->createElement(L"price", &spTheElem);
spTheElem->put_text(L"20"); spTheBook->appendChild(spTheElem, &spNewNode); spTheElem.Release(); spNewNode.Release();spXmldoc->createElement(L"memo", &spTheElem);
spTheElem->put_text(L"新书的更好看。"); spTheBook->appendChild(spTheElem, &spNewNode); spNewNode.Release(); spTheElem.Release();spRoot->appendChild(spTheBook, &spNewNode);
spNewNode.Release(); spTheBook.Release();spRoot->get_xml(&bstrXml);
AfxMessageBox(L"2, 新建一本书完成"); AfxMessageBox(bstrXml); --- 新建一本书完成 ---- --- 下面对《哈里波特》做一些修改。 ---- --- 查询找《哈里波特》---- CComPtr<IXMLDOMNode> spTheNode=NULL; spRoot->selectSingleNode(L"/books/book[name='哈里波特']", &spTheNode); hr=spTheNode.QueryInterface(&spTheBook); spTheNode.Release();spTheBook->get_xml(&bstrXml);
AfxMessageBox(L"3,《哈里波特》的XML"); AfxMessageBox(bstrXml);--- 此时修改这本书的价格 -----
CComPtr<IXMLDOMNodeList> spNodeList=NULL; CComPtr<IXMLDOMNode> spListItem=NULL; spTheBook->get_childNodes(&spNodeList); spNodeList->get_item(1, &spListItem); spNodeList.Release(); spListItem->put_text(L"15");--- 另外还想加一个属性id,值为B01 ----
CComVariant varId(L"B01"); spTheBook->setAttribute(L"id", varId); varId.Clear();spTheBook->get_xml(&bstrXml);
spTheBook.Release(); AfxMessageBox(L"4, 对《哈里波特》修改完成。"); AfxMessageBox(bstrXml); --- 对《哈里波特》修改完成。 ---- --- 要用id属性删除《三国演义》这本书 ---- spRoot->selectSingleNode(L"/books/book[@id='B02']", &spTheNode); hr=spTheNode.QueryInterface(&spTheBook); spTheNode.Release();spTheBook->get_xml(&bstrXml);
AfxMessageBox(L"5, 《三国演义》的XML"); AfxMessageBox(bstrXml);CComPtr<IXMLDOMNode> spParentNode=NULL;
spTheBook->get_parentNode(&spParentNode); spParentNode->removeChild(spTheBook, &spTheNode); spTheNode.Release(); spParentNode.Release(); spTheBook.Release();spRoot->get_xml(&bstrXml);
AfxMessageBox(L"6, 删除《三国演义》后的XML"); AfxMessageBox(bstrXml); --- 再将所有价格低于10的书删除 ---- spRoot->selectNodes(L"/books/book[price<10]", &spNodeList); CComQIPtr<IXMLDOMSelection> spSomeBooks=spNodeList; spNodeList.Release();spSomeBooks->removeAll();
spSomeBooks.Release();spXmldoc->get_xml(&bstrXml);
AfxMessageBox(L"7, 已经删除价格低于10的书"); AfxMessageBox(bstrXml);spRoot.Release();
bstrXml.Empty();//spXmldoc->save(varXmlFile); //保存xml。
} varXmlFile.ClearToZero(); }spXmldoc.Release();
CoUninitialize();
}a.xml
view plaincopy to clipboardprint?
<?xml version="1.0" encoding="UTF-8"?> <books> <book> <name>哈里波特</name> <price>10</price> <memo>这是一本很好看的书。</memo> </book> <book id="B02"> <name>三国演义</name> <price>10</price> <memo>四大名著之一。</memo> </book> <book id="B03"> <name>水浒</name> <price>6</price> <memo>四大名著之一。</memo> </book> <book id="B04"> <name>红楼</name> <price>5</price> <memo>四大名著之一。</memo> </book> </books> <?xml version="1.0" encoding="UTF-8"?> <books> <book> <name>哈里波特</name> <price>10</price> <memo>这是一本很好看的书。</memo> </book> <book id="B02"> <name>三国演义</name> <price>10</price> <memo>四大名著之一。</memo> </book> <book id="B03"> <name>水浒</name> <price>6</price> <memo>四大名著之一。</memo> </book> <book id="B04"> <name>红楼</name> <price>5</price> <memo>四大名著之一。</memo> </book> </books>
法六
在 Kenn Scribner 近期有关 XML 和 MSXML DOM 分析器的文章中,仅介绍了该分析器的部分功能。这些文章将 XML 作为一种进行了说明,但是并没有介绍 XML 分析器本身。现在,Kenn 将回过头来介绍 MSXML 分析器,并讲解处理 XML 文档和节点所需的基本知识:搜索特定的节点、插入节点和检索节点值。
MSXML 分析器基于 XML 文档对象模型,对于查看表 1 中所示的各种文档对象来说,它非常重要。这些对象直接出自 XML 规范本身。MSXML 还可以进一步将 XML DOM 对象合并到 COM 中。因此,弄清楚哪个 XML DOM 对象对应于哪个 MSXML COM 接口非常容易。例如,IXMLDOMNode 代表称为 Node 的 DOM 对象。
表 1. XML DOM 对象及其用途DOM 对象用途
DOMImplementation
一个查询对象,用于确定 DOM 支持的级别
DocumentFragment
表示树的一部分。
任一种情况下,搜索的结果都是一个 MSXML 节点对象 IXMLDOMNode。文档中必须存在该节点,否则搜索将失败。我的应用程序使用该节点作为一个全新 XML 节点的父级,该新节点是由 XML 文档对象创建的:
CComPtrIXMLDOMNode spXMLChildNode;
hr = spXMLDOM-createNode,
CComBSTR,
NULL,
&spXMLChildNode);
if)
throw Unable to create xmlchildnode XML node;
if
throw Unable to create xmlchildnode XML node;
如果分析器可以创建该节点,下一步就是将它放到 XML 树中。IXMLDOMNode::appendChild 正是完成这一任务的方法:
CComPtrIXMLDOMNode spInsertedNode;
hr = spXMLNode-appendChild;
if)
throw Unable to move xmlchildnode XML node;
if
throw Unable to move xmlchildnode XML node;
如果父节点的确将新创建的节点插入为其子级,将返回另一个 IXMLDOMNode 实例,该实例表示新的子节点。实际上,该新子节点和传递给 appendChild 的节点是同一个 XML 节点。由于在存在问题时附加的子节点的指针将为 Null,因此,检查该指针很有用。
到目前为止,我找到了一个特定的节点,并为它创建了一个新的子节点,下面,让我们看看如何处理属性。假定您要将该属性添加到新的子节点:
xml=fun
这并不难,但是您必须从 IXMLDOMNode 切换到 IXMLDOMElement,以便该子节点的元素特征。在实践中,这意味着您必须查询 IXMLDOMNode 接口的相关 IXMLDOMElement 接口,查明后,再调用 IXMLDOMElement::setAttribute:
CComQIPtrIXMLDOMElement spXMLChildElement;
spXMLChildElement = spInsertedNode;
if
throw Unable to query for xmlchildnode XML _
element interface;
hr = spXMLChildElement-setAttribute,
CComVariant);
if)
throw Unable to insert new attribute;
此时,已经修改了 XML 树,并创建了所需的树。应用程序可以在这个时候将文档保存到磁盘,或者执行其他任务。现在,让我们来搜索另一个节点并显示该节点所包含的值(文本)。您已经了解了如何搜索节点,因此,我们将直接讲解提取。
提取节点的关键在于使用 IXMLDOMNode::get_nodeTypedValue。可以使用 Microsoft 类型架构来标识节点所包含的,因此可以方便地存储浮点值、整数、字符串或该架构所支持的任何类型。可以使用 dt:type 属性来指定类型,如下所示:
model dt:type=stringSL-2/model
year dt:type=int1992/year
如果特定的节点具有指定的类型,就可以使用 get_nodeTypedValue 以该格式提取。如果未指定类型,将假定为文本,分析器将返回具有 BSTR 的 VARIANT。在本例中,这没有任何问题,因为我们要搜索的节点是一个实际上包含一个字符串的文本节点。在需要时,始终可以使用 atoi 等方法将字符串转换为其他形式。本例中,我们只是提取该字符串并显示它:
CComVariant varValue;
hr = spXMLNode-get_nodeTypedValue;
if)
throw Unable to retrieve xmltext text;
if
else
如果能够检索与节点关联的值,并且该值为 BSTR(预期的类型),我们将在屏幕上显示该文本。如果不能,将显示一条错误消息,不过,根据情况而定,可以方便地采取其他操作。
最后一项与 XML 有关的操作是将已更新的 XML 树保存到磁盘,这一任务是使用 IXMLDOMDocument::save 完成的:
hr = spXMLDOM-save);
if)
throw Unable to save updated XML document;
完成保存后,向屏幕写一条简短说明,并退出。
这个示例应用程序无论如何都算不上漂亮。您可以让自己的应用程序执行很多其他功能,但我希望您通过这个简短的示例了解到了如何从 C++ 程序使用 MSXML 分析器。该分析器本身是一个复杂的软件,无论怎样强调使用 MSDN Library 作为参考,都不能算是过份。该分析器公开了许多接口,这些接口通常会公开许多方法。即便如此,我在自己的项目中仍频繁地使用该分析器,在亲自编写了一些代码并进行试验后,我发现这个软件制作很精良 并且便于使用。我希望您也同样会发现该分析器和一般意义上的 XML 具有广泛的用途。