http://www.cnblogs.com/lingyun1120/archive/2011/11/02/2232709.html
XML在Win32程序方面应该没有在Web方面应用得多,很多Win32程序也只是用XML来存存配置信息而已,而且没有足够的好处的话还不如用ini。VC++里操作XML有两个库可以用:MSXML和XmlLite。MSXML又细分了两种接口:DOM和SAX2。XP没自带有XmlLite,只自带有2.x、3.x版的MSXML,不支持SAX2(需要MSXML 4.0以上),所以优先使用DOM。
DOM是以COM形式提供的,VC++里调用DOM可以分3种方法:
1、MFC里用CComPtr调用
2、SDK里直接调用DOM接口
3、SDK里用智能指针调用
这3种方法本质上是一样的,区别只不过在于需要编码的多少而已,用CComPtr可以极大的简化代码,下面是几个例子。
例子stocks.xml:
这个例子应该包含了XML最常见的特征了吧?
MFC
MFC里可以直接使用DOM,不需要手动添加额外的头文件,只需要在CWinApp::InitInstance()里调用CoInitialize(NULL)初始化COM,在CWinApp::ExitInstance里调用CoUninitialize()释放COM就行了。
对于<tag>text</tag>这样的节点,get_nodeValue会得到空,要得到"text"的话可以遍历子节点(只有一个子节点,它的nodeName为"#text",nodeType为NODE_TEXT,nodeValue就是"text");也可以用get_text直接得到"text",但是对于这样的节点<tag>text<childtag>childtext</childtag></tag>,get_text会同时得到"text"和"childtext",不过这样的节点应该是不允许的。
DOM里使用的字符串(BSTR)都是OLESTR类型,默认情况下OLESTR是Unicode字符,MFC里可以用COLE2CT把LPCOLESTR转换为LPCTSTR。
对于自己定义的XML,大多数时候不需要遍历,可以通过调用selectNodes、selectSingleNode指定XPath直接读取某个节点或属性:
XPath的语法可以参考XML文档或MSDN。
SDK
SDK中也可以使用智能指针,和MFC没太大区别,同样很方便,直接给代码:
#include <iostream >
#include <tchar. h >
#import <msxml3. dll >
//节点处理函数
void ProcessNode (MSXML2 :: IXMLDOMNodePtr spNode )
{
std :: cout << "nodeName: " << spNode - >nodeName ;
if (spNode - >nodeType == NODE_ATTRIBUTE || spNode - >nodeType == NODE_TEXT )
std :: cout << "\tnodeValue: " << _bstr_t (spNode - >nodeValue ) ;
std :: cout << std :: endl ;
if (spNode - >nodeType == NODE_ELEMENT )
{
MSXML2 :: IXMLDOMNamedNodeMapPtr spNameNodeMap = spNode - >attributes ;
for ( long i = 0 ; i ! = spNameNodeMap - >length ; ++i ) //遍历节点属性
ProcessNode (spNameNodeMap - >item [i ] ) ;
MSXML2 :: IXMLDOMNodeListPtr spNodeList = spNode - >childNodes ;
for ( long i = 0 ; i ! = spNodeList - >length ; ++i ) //遍历子节点
ProcessNode (spNodeList - >item [i ] ) ;
}
}
int _tmain ( int argc, _TCHAR * argv [ ] )
{
CoInitialize ( NULL ) ;
//读取XML
MSXML2 :: IXMLDOMDocumentPtr spXMLDoc ;
spXMLDoc. CreateInstance (__uuidof (MSXML2 :: DOMDocument30 ) ) ;
spXMLDoc - >load (L "stocks.xml" ) ;
MSXML2 :: IXMLDOMElementPtr spRoot = spXMLDoc - >documentElement ; //根节点
MSXML2 :: IXMLDOMNodeListPtr spNodeList = spRoot - >childNodes ;
for ( long i = 0 ; i ! = spNodeList - >length ; ++i ) //遍历子节点
ProcessNode (spNodeList - >item [i ] ) ;
//写入XML
spRoot - >selectSingleNode (L "/root/node1" ) - >text = L "newText" ;
spRoot - >selectSingleNode (L "/root/node2/childnode1/@attrib1" ) - >nodeValue = L "newValue" ;
MSXML2 :: IXMLDOMNodePtr spNewNode = spRoot - >selectSingleNode (L "/root/node2" ) - >appendChild (
spXMLDoc - >createNode (_variant_t (NODE_ELEMENT ), L "childnode3", L "" )
) ; //给node2创建新子节点childnode3
spNewNode - >text = L "childtext2" ;
MSXML2 :: IXMLDOMElementPtr spEle = spNewNode ;
spEle - >setAttribute (L "attrib1", _variant_t (L "value1" ) ) ; //添加新属性
spXMLDoc - >save (_variant_t (L "stocks.xml" ) ) ;
spNewNode. Release ( ) ;
spEle. Release ( ) ;
spNodeList. Release ( ) ;
spRoot. Release ( ) ;
spXMLDoc. Release ( ) ;
CoUninitialize ( ) ;
system ( "pause" ) ;
return 0 ;
}
DOM是以COM形式提供的,VC++里调用DOM可以分3种方法:
1、MFC里用CComPtr调用
2、SDK里直接调用DOM接口
3、SDK里用智能指针调用
这3种方法本质上是一样的,区别只不过在于需要编码的多少而已,用CComPtr可以极大的简化代码,下面是几个例子。
例子stocks.xml:
<?xml version="1.0" encoding="utf-8"?>
<root>
<node1>text1 </node1>
<node2>
<childnode1 attrib1="value1" attrib2="value2"/>
<childnode2 attrib1="value1" attrib2="value2">childtext1 </childnode2>
</node2>
</root>
<root>
<node1>text1 </node1>
<node2>
<childnode1 attrib1="value1" attrib2="value2"/>
<childnode2 attrib1="value1" attrib2="value2">childtext1 </childnode2>
</node2>
</root>
这个例子应该包含了XML最常见的特征了吧?
MFC
MFC里可以直接使用DOM,不需要手动添加额外的头文件,只需要在CWinApp::InitInstance()里调用CoInitialize(NULL)初始化COM,在CWinApp::ExitInstance里调用CoUninitialize()释放COM就行了。
//读取XML
CComPtr <IXMLDOMDocument > spDoc ; //DOM
spDoc. CoCreateInstance (CLSID_DOMDocument ) ;
VARIANT_BOOL vb ;
spDoc - >load (CComVariant (OLESTR ( "stocks.xml" ) ), &vb ) ; //加载XML文件
CComPtr <IXMLDOMElement > spRootEle ;
spDoc - >get_documentElement ( &spRootEle ) ; //根节点
CComPtr <IXMLDOMNodeList > spNodeList ;
spRootEle - >get_childNodes ( &spNodeList ) ; //子节点列表
long nLen ;
spNodeList - >get_length ( &nLen ) ; //子节点数
for ( long i = 0 ; i ! = nLen ; ++i ) //遍历子节点
{
CComPtr <IXMLDOMNode > spNode ;
spNodeList - >get_item (i, &spNode ) ;
ProcessNode (spNode ) ; //节点处理函数
}
//写入XML
CComPtr <IXMLDOMNode > spNode ;
spRootEle - >selectSingleNode (OLESTR ( "/root/node1" ), &spNode ) ;
spNode - >put_text (OLESTR ( "newText" ) ) ; //写入text
spRootEle - >selectSingleNode (OLESTR ( "/root/node2/childnode1/@attrib1" ), &spNode ) ;
spNode - >put_nodeValue (CComVariant (OLESTR ( "newValue" ) ) ) ; //写入value
CComPtr <IXMLDOMNode > spNewNode ;
spDoc - >createNode (CComVariant (NODE_ELEMENT ), OLESTR ( "childnode3" ), OLESTR ( "" ), &spNewNode ) ; //创建新节点
spRootEle - >selectSingleNode (OLESTR ( "/root/node2" ), &spNode ) ;
spNode - >appendChild (spNewNode, &spNewNode ) ; //将新节点加为node2的子节点
spNewNode - >put_text (OLESTR ( "childtext2" ) ) ; //写入新节点text
CComQIPtr <IXMLDOMElement > spEle = spNewNode ; //注意这里使用CComQIPtr
spEle - >setAttribute (OLESTR ( "attrib1" ), CComVariant (OLESTR ( "value1" ) ) ) ; //给新节点添加属性
spDoc - >save (CComVariant (OLESTR ( "stocks.xml" ) ) ) ;
//节点处理函数
void ProcessNode (CComPtr <IXMLDOMNode > & spNode )
{
CComBSTR bsNodeName ;
spNode - >get_nodeName ( &bsNodeName ) ; //节点名
AfxMessageBox (COLE2CT (bsNodeName ) ) ;
CComVariant varVal ;
spNode - >get_nodeValue ( &varVal ) ; //节点值
AfxMessageBox (COLE2CT (varVal. bstrVal ) ) ;
DOMNodeType eNodeType ;
spNode - >get_nodeType ( &eNodeType ) ;
if (eNodeType == NODE_ELEMENT ) //只有NODE_ELEMENT类型才能包含有属性和子节点
{
//递归遍历节点属性
CComPtr <IXMLDOMNamedNodeMap > spNameNodeMap ;
spNode - >get_attributes ( &spNameNodeMap ) ;
long nLength ;
spNameNodeMap - >get_length ( &nLength ) ;
for ( long i = 0 ; i ! = nLength ; ++i )
{
CComPtr <IXMLDOMNode > spNodeAttrib ; //注意属性也是一个IXMLDOMNode
spNameNodeMap - >get_item (i, &spNodeAttrib ) ;
ProcessNode (spNodeAttrib ) ;
}
//递归遍历子节点
CComPtr <IXMLDOMNodeList > spNodeList ;
spNode - >get_childNodes ( &spNodeList ) ;
spNodeList - >get_length ( &nLength ) ;
for ( long i = 0 ; i ! = nLength ; ++i )
{
CComPtr <IXMLDOMNode > spChildNode ;
spNodeList - >get_item (i, &spChildNode ) ;
ProcessNode (spChildNode ) ;
}
}
}
CComPtr <IXMLDOMDocument > spDoc ; //DOM
spDoc. CoCreateInstance (CLSID_DOMDocument ) ;
VARIANT_BOOL vb ;
spDoc - >load (CComVariant (OLESTR ( "stocks.xml" ) ), &vb ) ; //加载XML文件
CComPtr <IXMLDOMElement > spRootEle ;
spDoc - >get_documentElement ( &spRootEle ) ; //根节点
CComPtr <IXMLDOMNodeList > spNodeList ;
spRootEle - >get_childNodes ( &spNodeList ) ; //子节点列表
long nLen ;
spNodeList - >get_length ( &nLen ) ; //子节点数
for ( long i = 0 ; i ! = nLen ; ++i ) //遍历子节点
{
CComPtr <IXMLDOMNode > spNode ;
spNodeList - >get_item (i, &spNode ) ;
ProcessNode (spNode ) ; //节点处理函数
}
//写入XML
CComPtr <IXMLDOMNode > spNode ;
spRootEle - >selectSingleNode (OLESTR ( "/root/node1" ), &spNode ) ;
spNode - >put_text (OLESTR ( "newText" ) ) ; //写入text
spRootEle - >selectSingleNode (OLESTR ( "/root/node2/childnode1/@attrib1" ), &spNode ) ;
spNode - >put_nodeValue (CComVariant (OLESTR ( "newValue" ) ) ) ; //写入value
CComPtr <IXMLDOMNode > spNewNode ;
spDoc - >createNode (CComVariant (NODE_ELEMENT ), OLESTR ( "childnode3" ), OLESTR ( "" ), &spNewNode ) ; //创建新节点
spRootEle - >selectSingleNode (OLESTR ( "/root/node2" ), &spNode ) ;
spNode - >appendChild (spNewNode, &spNewNode ) ; //将新节点加为node2的子节点
spNewNode - >put_text (OLESTR ( "childtext2" ) ) ; //写入新节点text
CComQIPtr <IXMLDOMElement > spEle = spNewNode ; //注意这里使用CComQIPtr
spEle - >setAttribute (OLESTR ( "attrib1" ), CComVariant (OLESTR ( "value1" ) ) ) ; //给新节点添加属性
spDoc - >save (CComVariant (OLESTR ( "stocks.xml" ) ) ) ;
//节点处理函数
void ProcessNode (CComPtr <IXMLDOMNode > & spNode )
{
CComBSTR bsNodeName ;
spNode - >get_nodeName ( &bsNodeName ) ; //节点名
AfxMessageBox (COLE2CT (bsNodeName ) ) ;
CComVariant varVal ;
spNode - >get_nodeValue ( &varVal ) ; //节点值
AfxMessageBox (COLE2CT (varVal. bstrVal ) ) ;
DOMNodeType eNodeType ;
spNode - >get_nodeType ( &eNodeType ) ;
if (eNodeType == NODE_ELEMENT ) //只有NODE_ELEMENT类型才能包含有属性和子节点
{
//递归遍历节点属性
CComPtr <IXMLDOMNamedNodeMap > spNameNodeMap ;
spNode - >get_attributes ( &spNameNodeMap ) ;
long nLength ;
spNameNodeMap - >get_length ( &nLength ) ;
for ( long i = 0 ; i ! = nLength ; ++i )
{
CComPtr <IXMLDOMNode > spNodeAttrib ; //注意属性也是一个IXMLDOMNode
spNameNodeMap - >get_item (i, &spNodeAttrib ) ;
ProcessNode (spNodeAttrib ) ;
}
//递归遍历子节点
CComPtr <IXMLDOMNodeList > spNodeList ;
spNode - >get_childNodes ( &spNodeList ) ;
spNodeList - >get_length ( &nLength ) ;
for ( long i = 0 ; i ! = nLength ; ++i )
{
CComPtr <IXMLDOMNode > spChildNode ;
spNodeList - >get_item (i, &spChildNode ) ;
ProcessNode (spChildNode ) ;
}
}
}
对于<tag>text</tag>这样的节点,get_nodeValue会得到空,要得到"text"的话可以遍历子节点(只有一个子节点,它的nodeName为"#text",nodeType为NODE_TEXT,nodeValue就是"text");也可以用get_text直接得到"text",但是对于这样的节点<tag>text<childtag>childtext</childtag></tag>,get_text会同时得到"text"和"childtext",不过这样的节点应该是不允许的。
DOM里使用的字符串(BSTR)都是OLESTR类型,默认情况下OLESTR是Unicode字符,MFC里可以用COLE2CT把LPCOLESTR转换为LPCTSTR。
对于自己定义的XML,大多数时候不需要遍历,可以通过调用selectNodes、selectSingleNode指定XPath直接读取某个节点或属性:
CComPtr
<IXMLDOMDocument
>
spDoc
;
//DOM
spDoc. CoCreateInstance (CLSID_DOMDocument ) ;
VARIANT_BOOL vb ;
spDoc - >load (CComVariant (OLESTR ( "stocks.xml" ) ), &vb ) ; //加载XML文件
CComPtr <IXMLDOMElement > spRootEle ;
spDoc - >get_documentElement ( &spRootEle ) ; //根节点
CComPtr <IXMLDOMNodeList > spNodeList ;
CComPtr <IXMLDOMNode > spNode ;
spRootEle - >selectNodes (OLESTR ( "/root/node2/*" ), &spNodeList ) ; //得到node2下的所有子节点
spRootEle - >selectSingleNode (OLESTR ( "/root/node2/childnode1/@attrib1" ), &spNode ) ; //得到childnode1的attrib1属性
spDoc. CoCreateInstance (CLSID_DOMDocument ) ;
VARIANT_BOOL vb ;
spDoc - >load (CComVariant (OLESTR ( "stocks.xml" ) ), &vb ) ; //加载XML文件
CComPtr <IXMLDOMElement > spRootEle ;
spDoc - >get_documentElement ( &spRootEle ) ; //根节点
CComPtr <IXMLDOMNodeList > spNodeList ;
CComPtr <IXMLDOMNode > spNode ;
spRootEle - >selectNodes (OLESTR ( "/root/node2/*" ), &spNodeList ) ; //得到node2下的所有子节点
spRootEle - >selectSingleNode (OLESTR ( "/root/node2/childnode1/@attrib1" ), &spNode ) ; //得到childnode1的attrib1属性
XPath的语法可以参考XML文档或MSDN。
SDK
SDK中也可以使用智能指针,和MFC没太大区别,同样很方便,直接给代码:
#include <iostream >
#include <tchar. h >
#import <msxml3. dll >
//节点处理函数
void ProcessNode (MSXML2 :: IXMLDOMNodePtr spNode )
{
std :: cout << "nodeName: " << spNode - >nodeName ;
if (spNode - >nodeType == NODE_ATTRIBUTE || spNode - >nodeType == NODE_TEXT )
std :: cout << "\tnodeValue: " << _bstr_t (spNode - >nodeValue ) ;
std :: cout << std :: endl ;
if (spNode - >nodeType == NODE_ELEMENT )
{
MSXML2 :: IXMLDOMNamedNodeMapPtr spNameNodeMap = spNode - >attributes ;
for ( long i = 0 ; i ! = spNameNodeMap - >length ; ++i ) //遍历节点属性
ProcessNode (spNameNodeMap - >item [i ] ) ;
MSXML2 :: IXMLDOMNodeListPtr spNodeList = spNode - >childNodes ;
for ( long i = 0 ; i ! = spNodeList - >length ; ++i ) //遍历子节点
ProcessNode (spNodeList - >item [i ] ) ;
}
}
int _tmain ( int argc, _TCHAR * argv [ ] )
{
CoInitialize ( NULL ) ;
//读取XML
MSXML2 :: IXMLDOMDocumentPtr spXMLDoc ;
spXMLDoc. CreateInstance (__uuidof (MSXML2 :: DOMDocument30 ) ) ;
spXMLDoc - >load (L "stocks.xml" ) ;
MSXML2 :: IXMLDOMElementPtr spRoot = spXMLDoc - >documentElement ; //根节点
MSXML2 :: IXMLDOMNodeListPtr spNodeList = spRoot - >childNodes ;
for ( long i = 0 ; i ! = spNodeList - >length ; ++i ) //遍历子节点
ProcessNode (spNodeList - >item [i ] ) ;
//写入XML
spRoot - >selectSingleNode (L "/root/node1" ) - >text = L "newText" ;
spRoot - >selectSingleNode (L "/root/node2/childnode1/@attrib1" ) - >nodeValue = L "newValue" ;
MSXML2 :: IXMLDOMNodePtr spNewNode = spRoot - >selectSingleNode (L "/root/node2" ) - >appendChild (
spXMLDoc - >createNode (_variant_t (NODE_ELEMENT ), L "childnode3", L "" )
) ; //给node2创建新子节点childnode3
spNewNode - >text = L "childtext2" ;
MSXML2 :: IXMLDOMElementPtr spEle = spNewNode ;
spEle - >setAttribute (L "attrib1", _variant_t (L "value1" ) ) ; //添加新属性
spXMLDoc - >save (_variant_t (L "stocks.xml" ) ) ;
spNewNode. Release ( ) ;
spEle. Release ( ) ;
spNodeList. Release ( ) ;
spRoot. Release ( ) ;
spXMLDoc. Release ( ) ;
CoUninitialize ( ) ;
system ( "pause" ) ;
return 0 ;
}