如何在VC中使用XML
Posted
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了如何在VC中使用XML相关的知识,希望对你有一定的参考价值。
ML在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 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>
这个例子应该包含了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);
对于<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属性
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);
MSXML2::IXMLDOMNodeListPtr spNodeList = spNode->childNodes;
for (long i = 0; i != spNodeList->length; ++i) //遍历子节点
ProcessNode(spNodeList->item);
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);
//写入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;
参考技术A 利用MSXML解析XML文本(1)
XML DOM (文档对象模型)对象提供了一个标准的方法来操作存储在XML文档中的信息,这就是DOM应用编程接口(API)函数。它是应用程序和XML文档之间的桥梁。DOM包含两个关键的抽象概念:一个是树状的层次结构,另一个是用来表示文档内容和结构的节点集合。树状层次包括了所有节点,节点本身也可以包含其他的节点。这样的好处是可以通过这个层次结构来找到并修改某一特定节点的信息。
----微软的MSXML解析器读取一个XML文档,然后把它的内容解析到一个抽象的信息容器中,该信息容器被称为节点(NODES)。这些节点代表文档的结构和内容,并允许应用程序来操作文档中的信息而不需要知道XML的语义。一个文档被解析后,它的节点能够在任何时候被浏览而不需要保持一定的顺序。
----对开发人员来说,最重要的编程对象是DOMDocument。 DOMDocument对象通过暴露的属性和方法来允许浏览、查询和修改XML文档的内容和结构。
----本文主要介绍DOM的结构和应用,同时用VC编程语言给出了通过MSXML进行XML解析的实例。
DOMDocument对象的结构和应用
----文档对象的创建
HRESULT hr;
IXMLDomDocument* pXMLDoc;
IXMLDOMNode* pXDN;
//COM的初始化
Hr=CoInitialize(NULL);
/*得到关于IXMLDOMDocument
接口的指针pXMLDOC*/
hr=CoCreateInstance(CLSID_DOMDocument,NULL,
CLSCTX_INPPROC_SERVER,IID_IXMLDOMDocument,
(void**)&pXMLDoc);
//得到关于IXMLDOMNode接口的指针pXDN
hr=pXMLDoc->QueryInterface(IID_IXMLDOM
Node,(void**)&pXDN);
----在MSXML解析器的使用过程中,我们可以使用文档中的CreateElement方法创建一个节点来装载和保存XML文件,也可以通过Load或者是 LoadXML方法从一个指定的URL来装载一个XML文档。Load(LoadXML)方法带有两个参数:第一个参数xmlSource表示需要被解析的文档,第二个参数isSuccessful表示文档装载是否成功。
----文档对象的保存
----Save方法是用来把文档保存到一个指定的位置。Save方法中参数destination用来表示需要被保存的对象的类型,对象可以是一个文件、一个ASP Response方法、一个XML文档对象,或者是一个能够支持持久保存(persistence)的客户对象。下面是使用Save方法的一个例子程序的部分代码:
BOOL DOMDocSaveLocation()
BOOL bResult = FALSE;
IXMLDOMDocument *pIXMLDOMDocument = NULL;
HRESULT hr;
try
_variant_t varString = _T(“D:\\sample.xml");
/* 这里省略了创建一个DOMDocument
对象和装载XML文档的代码*/
//将文档保存到D:\\sample.xml中去
hr=pIXMLDOMDocument->save(varString);
if(SUCCEEDED(hr))
bResult = TRUE;
catch(...)
DisplayErrorToUser();
/*这里省略了释放对IXMLDOMDocument
接口的引用的代码*/
return bResult;
----设置解析标志
----在解析过程中,我们需要得到和设置解析标志。利用不同的解析标志,我们可以用不同的方法来解析一个XML文档。XML标准允许解析器验证或者不验证文档,允许不验证文档的解析过程跳过对外部资源的提取,还可以设置标志来表明是否要从文档中移去多余的空格。DOMDocument对象暴露了如下几个属性,允许用户在运行的时候利用它们改变解析器的行为。
Async属性方法:get_Async和put_Async。
ValidateOnParse属性方法:get_ValidateOnParse和 put_ValidateOnParse。
ResolveExternals属性方法:get_ ResolveExternals和put_ ResolveExternals。
PreserveWhiteSpace属性方法:get_ PreserveWhiteSpace和put_ PreserveWhiteSpace。
----每一个属性可以接受或者返回一个Boolean值。缺省情况下,Async、ValidateOnParse、ResolveExternals的值为TRUE,PreserveWhiteSpace的值跟 XML文档的设置有关,如果XML文档中设置了xml:space属性的话,该值为FALSE。
----在文档解析过程中可以收集到以下的信息:
doctype(文档类型):是用来定义文档格式的DTD文件。如果XML文档没有相关的 DTD文档的话,它就返回NULL。
implementation(实现):表示该文档的实现,用来指出当前文档所支持的XML的版本。
parseError(解析错误):指出在解析过程中最后所发生的错误。
readyState(状态信息):表示XML文档的状态信息。readyState对于异步使用微软的XML 解析器来说重要的作用是提高了性能。当异步装载XML文档的时候,程序可能需要检查解析的状态,MSXML提供了4个状态,分别为正在状态、已经状态、正在解析和解析完成。
url(统一资源定位):表示正在被装载和解析的XML文档的URL的情况。如果该文档是在内存中建立的话,这个属性返回NULL值。
----节点的操作
---- 在得到文档树结构以后,我们可以操作树中的每一个节点,一般通过两个方法得到树中的节点,分别为nodeFromID和getElementsByTagName。
---- nodeFromID包括两个参数,第一个参数idString用来表示ID值,第二个参数node返回指向和该ID相匹配的节点的接口指针。根据XML的技术规定,每一个XML文档中的ID值必须是唯一的,而且一个元素(element)只能和一个ID 相关联。
----getElementsByTagName方法有两个参数,第一个参数 tagName表示需要查找的元素(Element)名称,如果tagName为“*”则返回文档中所有的元素。第二个参数为resultList,它实际是指向接口IXMLDOMNodeList的指针,用来返回和 tagName(标签名字)相关的所有节点的集合。
----下面是相关例子程序的部分代码:
IXMLDOMDocument *pIXMLDOMDocument = NULL;
wstring strFindText (_T(“author"));
IXMLDOMNodeList *pIDOMNodeList = NULL;
IXMLDOMNode *pIDOMNode = NULL;
long value;
BSTR bstrItemText;
HRESULT hr;
try
/*此处省略创建一个DOMDocument
文档对象并装载具体文档的代码*/
/*下面的代码用来得到一个和标签名称本回答被提问者和网友采纳 参考技术B CMarkup类
如何在 vc++ mfc 中使用非静态方法创建线程
【中文标题】如何在 vc++ mfc 中使用非静态方法创建线程【英文标题】:how to create a thread using a non static method in vc++ mfc 【发布时间】:2011-05-14 11:12:23 【问题描述】:我正在使用这个调用创建一个线程:
m_pThread=AfxBeginThread(read_data,(LPVOID)hSerial);
read_data
是我班级中的静态方法。
但我想调用一个非静态方法并创建一个线程。
因为我想在这个线程和我的一个类方法之间共享一个变量。
我尝试使用静态变量,但它给出了一些错误。
【问题讨论】:
【参考方案1】:你不能使用函数的非静态成员作为线程过程来创建线程:原因是类的所有非静态方法都有一个隐式的第一个参数,这是指针 this。
这个
class foo
void dosomething();
;
其实是
class foo
void dosomething(foo* this);
;
因此,函数签名与线程过程所需的签名不匹配。您可以将静态方法用作线程过程并将 this 指针传递给它。这是一个例子:
class foo
CWindThread* m_pThread;
HANDLE hSerial;
static UINT MyThreadProc(LPVOID pData);
void Start();
;
void foo::Start()
m_pThread=AfxBeginThread(MyThreadProc,(LPVOID)this);
UINT foo::MyThreadProc(LPVOID pData)
foo* self = (foo*)pData;
// now you can use self as it was this
ReadFile(self->hSerial, ...);
return 0;
【讨论】:
【参考方案2】:我不会重复马吕斯所说的,但会补充说我使用以下内容:
class foo
CWindThread* m_pThread;
HANDLE hSerial;
static UINT _threadProc(LPVOID pData);
UINT MemberThreadProc();
void Start();
;
void foo::Start()
m_pThread=AfxBeginThread(_threadProc,(LPVOID)this);
UINT foo::MyThreadProc(LPVOID pData)
foo* self = (foo*)pData;
// call class instance member
return self->MemberThreadProc();
UINT foo::MemberThreadProc()
// do work
ReadFile(hSerial, ...);
return 0;
每次在 MFC 应用程序的类中使用线程时,我都会遵循这种模式。这样我就可以方便地让所有成员都像我一样在课堂上。
【讨论】:
以上是关于如何在VC中使用XML的主要内容,如果未能解决你的问题,请参考以下文章