在现代软件开发中,XML文件被广泛用于数据交换和配置管理。C++作为一种强大的编程语言,提供了多种方式来解析XML文件。本文将介绍如何使用C++和MSXML库来解析XML文档,并展示如何使用ATL智能指针来简化内存管理。
在开始之前,需要根据所使用的开发环境进行相应的设置。这里将介绍如何在Visual Studio .NET、BorlandC++Builder以及嵌入式Visual C++ (EVC)中进行设置。
Visual Studio .NET自带了所需的库和环境,因此无需额外设置。
在BorlandC++Builder中,需要在项目选项的目录中添加ATL的头文件路径。
对于EVC,需要下载并包含一个免费的STL端口,该端口由Giuseppe Govi制作。
在C++代码中,需要包含以下头文件:
#include <windows.h>
#include <msxml.h>
#include <objsafe.h>
#include <objbase.h>
#include <atlbase.h>
为了处理警告,可以使用以下代码:
#pragma warning( push )
#pragma warning( disable: 4018 4786)
#include <string>
#pragma warning( pop )
使用命名空间std以简化代码:
using namespace std;
在WinMain的开始处初始化COM库,并在结束处进行反初始化:
CoInitializeEx(NULL, COINIT_MULTITHREADED);
// ...
CoUninitialize();
对于桌面Win32编译,可以使用CoInitialize(NULL),因为它在Win95上也能工作,不需要定义_WIN32_WINNT。但是它在PocketPC上不可用。
使用ATL的智能指针来避免手动调用Release()。以下代码展示了如何加载XML文档:
CComPtr<IXMLDOMDocument> iXMLDoc;
iXMLDoc.CoCreateInstance(__uuidof(DOMDocument));
#ifdef UNDER_CE
iXMLDoc->put_async(VARIANT_FALSE);
CComQIPtr<IObjectSafety, &IID_IObjectSafety> isafe(iXMLDoc);
if (isafe) {
DWORD dwSupported, dwEnabled;
isafe->GetInterfaceSafetyOptions(IID_IXMLDOMDocument, &dwSupported, &dwEnabled);
isafe->SetInterfaceSafetyOptions(IID_IXMLDOMDocument, dwSupported, 0);
}
#endif
VARIANT_BOOL bSuccess = false;
iXMLDoc->load(CComVariant(url), &bSuccess);
如果需要从BSTR加载,可以使用以下代码:
iXMLDoc->loadXML(CComBSTR(s), &bSuccess);
获取根元素:
CComPtr<IXMLDOMElement> iRootElm;
iXMLDoc->get_documentElement(&iRootElm);
为了简化元素的访问和迭代,可以编写一个辅助类TElem。以下是一个示例XML文档:
<?xml version="1.0" encoding="utf-16" ?>
<root desc="Simple Prog">
<text>Hello World</text>
<layouts>
<lay pos="15" bold="true" />
<layoff pos="12" />
<layin pos="17" />
</layouts>
</root>
以下是如何使用TElem类:
TElem eroot(iRootElm);
wstring desc = eroot.attr(L"desc");
TElem etext = eroot.subnode(L"text");
wstring s = etext.val();
s = eroot.subval(L"text");
TElem elays = eroot.subnode(L"layouts");
for (TElem e = elays.begin(); e != elays.end(); e++) {
int pos = e.attrInt(L"pos", -1);
bool bold = e.attrBool(L"bold", false);
wstring id = e.name();
}
struct TElem {
CComPtr<IXMLDOMElement> elem;
CComPtr<IXMLDOMNodeList> nlist;
int pos;
long clen;
TElem() : elem(0), nlist(0), pos(-1), clen(0) {}
TElem(int _clen) : elem(0), nlist(0), pos(-1), clen(_clen) {}
TElem(CComPtr<IXMLDOMElement> _elem) : elem(_elem), nlist(0), pos(-1), clen(0) { get(); }
TElem(CComPtr<IXMLDOMNodeList> _nlist) : elem(0), nlist(_nlist), pos(0), clen(0) { get(); }
void get() {
if (pos != -1) {
elem = 0;
CComPtr<IXMLDOMNode> inode;
nlist->get_item(pos, &inode);
if (inode == 0) return;
DOMNodeType type; inode->get_nodeType(&type);
if (type != NODE_ELEMENT) return;
CComQIPtr<IXMLDOMElement> e(inode);
elem = e;
}
clen = 0;
if (elem != 0) {
CComPtr<IXMLDOMNodeList> iNodeList;
elem->get_childNodes(&iNodeList);
iNodeList->get_length(&clen);
}
}
wstring name() const {
if (!elem) return L"";
CComBSTR bn; elem->get_tagName(&bn);
return wstring(bn);
}
wstring attr(const wstring name) const {
if (!elem) return L"";
CComBSTR bname(name.c_str());
CComVariant val(VT_EMPTY);
elem->getAttribute(bname, &val);
if (val.vt == VT_BSTR) return val.bstrVal;
return L"";
}
bool attrBool(const wstring name, bool def) const {
wstring a = attr(name);
if (a == L"true" || a == L"TRUE") return true;
else if (a == L"false" || a == L"FALSE") return false;
else return def;
}
int attrInt(const wstring name, int def) const {
wstring a = attr(name);
int i, res = swscanf(a.c_str(), L"%i", &i);
if (res == 1) return i;
else return def;
}
wstring val() const {
if (!elem) return L"";
CComVariant val(VT_EMPTY);
elem->get_nodeTypedValue(&val);
if (val.vt == VT_BSTR) return val.bstrVal;
return L"";
}
TElem subnode(const wstring name) const {
if (!elem) return TElem();
for (TElem c = begin(); c != end(); c++) {
if (c.name() == name) return c;
}
return TElem();
}
wstring subval(const wstring name) const {
if (!elem) return L"";
TElem c = subnode(name);
return c.val();
}
TElem begin() const {
if (!elem) return TElem();
CComPtr<IXMLDOMNodeList> iNodeList;
elem->get_childNodes(&iNodeList);
return TElem(iNodeList);
}
TElem end() const {
return TElem(clen);
}
TElem operator++(int) {
if (pos != -1) { pos++; get(); }
return *this;
}
bool operator!=(const TElem &e) const {
return pos != e.clen;
}
};