65.9K
CodeProject 正在变化。 阅读更多。
Home

使用 MSXML 读取 XML 文档

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.40/5 (31投票s)

2003年6月8日

公共领域

2分钟阅读

viewsIcon

376359

downloadIcon

4756

如何使用 MSXML,以现代 C++/模板 方式读取 XML 文档

引言

现在每个人都需要解析 XML。我发现很难找到好的 C++ 示例源代码——大多数代码似乎都是用一种没有模板的过时风格编写的,或者是针对 C# 或 Visual Basic 的。因此,本文提供了一个例子。

解析是使用 MSXML 完成的,我使用 ATL“智能指针”来避免手动释放所有内容。请注意,MSXML 始终是 Unicode。尝试将其与多字节/ASCII 一起使用会浪费大量精力。

随附的源代码包含用于嵌入式 Visual C++ (.vcw .vcp)、Visual C++ .NET (.sln .vcproj) 和 Borland C++Builder5 (.bpr .bpf) 的项目文件。但没有用于 Visual C++6 的项目文件,因为它没有附带足够新的 MSXML 标头。

PocketPC 注意事项:我使用 XML 来存储我的配置文件。它们每个都已增长到大约 80k,在 PocketPC 上,解析它们需要 2 秒。因此,我实际上将其解析为更有效的内存块结构,并将此内存块写入磁盘。这样,只有在发生任何更改时才需要重新解析。

准备工作

设置取决于您使用的开发环境

  • Visual Studio .NET -- 一切都很好
  • Borland C++ Builder -- 在 Project > Options > Directories 下,添加($BCB)\include\atl
  • eMbedded Visual C++ (EVC) -- 下载 Giuseppe Govi 制作的免费 STL 端口,并将其放入项目的子目录 "stl_eVC" 中
#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 )
using namespace std;

(警告禁用程序仅适用于 EVC,否则会生成虚假警告。)

此外,事先执行 CoInitializeEx(NULL,COINIT_MULTITHREADED);(通常在 WinMain 的开头),并在之后执行 CoUninitialize();(通常在 WinMain 的结尾)。

实际上,为桌面 win32 编译时,CoInitialize(NULL) 更容易,因为它可以在 Win'95 上运行,因此不需要您定义 _WIN32_WINNT。但它在 PocketPC 上不可用。

XML 解析

这是加载 XML 文档的方法。它使用 ATL 安全指针的魔力,避免了之后需要 Release() 所有内容。(为简单起见,省略了错误检查。)

CComPtr<IXMLDOMDocument> iXMLDoc;
iXMLDoc.CoCreateInstance(__uuidof(DOMDocument));
     
#ifdef UNDER_CE
// Following is a bugfix for PocketPC.
iXMLDoc->put_async(VARIANT_FALSE);
CComQIPtr<IObjectSafety,&IID_IObjectSafety> isafe(iXMLDoc);
if (iSafety) 
{ DWORD dwSupported, dwEnabled; 
  isafe->GetInterfaceSafetyOptions(IID_IXMLDOMDocument,
                                   &dwSupported,&dwEnabled);
  isafe->SetInterfaceSafetyOptions(IID_IXMLDOMDocument,
                                   dwSupported,0);
}
#endif

// Load the file. 
VARIANT_BOOL bSuccess=false;
// Can load it from a url/filename...
iXMLDoc->load(CComVariant(url),&bSuccess);
// or from a BSTR...
//iXMLDoc->loadXML(CComBSTR(s),&bSuccess);

// Get a pointer to the root
CComPtr<IXMLDOMElement> iRootElm;
iXMLDoc->get_documentElement(&iRootElm);

// Thanks to the magic of CComPtr, we never need call
// Release() -- that gets done automatically.

至于访问元素和遍历它们,我编写了一个小助手类 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");
// returns "Simple Prog"

TElem etext = eroot.subnode(L"text");
wstring s = etext.val();
// returns "Hello World"
s = eroot.subval(L"text");
// This is a shorter way to achieve the same thing

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);
  // we suggest defaults, in case the attribute is missing
  wstring id = e.name();
  // returns "lay" or "layoff" or "layin"
}

同样,无需释放 TElem - 这是自动完成的。TElem 中的完整方法列表

// TElem -- a simple class to wrap up IXMLDomElement
// and to iterate its children.

wstring TElem::name() const;
// in <item>stuff</item> it returns "item"

wstring TElem::val() const;
// in <item>stuff</item> it returns "stuff"

wstring TElem::attr(const wstring name) const;
// in <item name="hello">stuff</item> it returns "hello"
// int x=e.attrInt(L"a",2)
// bool b=e.attrBool(L"a",true),
// We supply defaults in case the attribute was absent.

TElem TElem::subnode(const wstring name) const;
// in <item><a>hello</a><name>there</name></item>
// it returns the TElem <name>there</name>

wstring TElem::subval(const wstring name) const;
// in <item><a>hello</a><name>there</name></item>
// it returns "there"

for (TElem c=e.begin(); c!=e.end(); c++) {...}
// iterates over the subnodes

TElem 的源代码

请注意,在此源代码中使用了 CComPtrCComQIPtrCComBSTR。这些是 ATL 提供的可爱的“安全指针”,意味着我们不必担心 Release()

我有点吝啬,所以在 TElem 中包含了迭代器功能,而不是编写单独的 TElemIterator 类。

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;
  }
};
© . All rights reserved.