MSXML を利用して XMLファイル を解析するには

解説

MSXML を利用して XMLファイル を解析する方法をまとめます。

便利機能

・IXMLDOM~Ptr を使う
MSXMLの処理においては、IXMLDOM~型のオブジェクトを扱います。
IXMLDOM~型には、型名の語尾にPtrが「付く」型と、「付かない」型があります。
型名の語尾にPtrが「付く」型は、スマートポインタ型なので、特に理由がない限り、型名の語尾にPtrが「付く」型を使います。
型名の語尾にPtrが「付かない」型を使う場合には、オブジェクトが不要になった際には Release関数を呼び出す、型変換は QueryInterface関数を通して行う、等が必要になります。
・CComVariant、CComBSTRを使う
MSXMLの処理においては、BSTR型やVARIANT型のオブジェクトを扱います。
BSTR型やVARIANT型には、ラッパークラスである、CComVariant型、CComBSTR型があります。特に理由がない限り、ラッパークラスである、CComVariant型、CComBSTR型を使います。
BSTR型やVARIANT型を使う場合には、領域の確保処理の記述、領域の開放処理の記述、型変換処理の記述、等が必要になります。
準備

・MSXML.DLL のインポート
#import "msxml.dll" named_guids raw_interfaces_only
をファイルヘッダー部に追加します。
これにより、MSXMLの処理が使えるようになります。
・import ディレクティブ:コンパイラは、指定されたファイルを利用可能にするヘッダーファイルを自動生成し、生成されたヘッダーを読み込みます。
・msxml.dll:msxml処理モジュールファイル。MSXML.DLLには、複数のバージョンがあります。必要に応じて、msxml.dll を msxml2.dll , msxml3.dll , msxml4.dll , msxml5.dll , msxml6.dll 等に置き換えます。
・named_guidsオプション:コンパイラは、指定されたファイルを利用可能にするヘッダーファイルに、GUID型の CLSIDやIIDの定義を生成します。
・raw_interfaces_onlyオプション:コンパイラは、指定されたファイルを利用可能にするヘッダーファイルにCOMインターフェイスの定義を生成します。
・atlbase.h のインクルード
#include 
をファイルヘッダー部に追加します。
これにより、CComVariant型、CComBSTR型が使えるようになります。
・COMの初期化処理、COMの終了処理
MSXMLの処理を使うためには、COMの初期化が必要です。
CoInitialize(NULL);
をアプリケーションの開始処理部に、
CoUninitialize();
をアプリケーションの終了処理部に、
追加します。
手続き

・DOMドキュメントの作成
IXMLDOMDocumentPtr::CreateInstance 関数を利用し、IXMLDOMDocumentPtrオブジェクトを作成する。
・XMLファイル読み込み
IXMLDOMDocumentPtr::load 関数を利用して、ファイルを読み込みます。
load 関数に先立って、IXMLDOMDocumentPtr::put_async( VARIANT_FALSE ) を呼び出し、load関数が、ロードを完了するまで待つようにします。
・ノード、属性の探索、値の取得
各種 get~関数を利用して、ノード、属性を探索し、また、値の取得をします。
サンプルコード


#import "msxml6.dll" named_guids raw_interfaces_only
#include 	// CComVariant, CComBSTR

#include 
#include 
#include 

bool GetXmlValue(	const IXMLDOMDocumentPtr&	pXMLDOMDocument,
					const CComBSTR&				bstrTagName,
					const CComBSTR&				bstrAttributeName,
					std::vector&		rvbstrValue )
{
	// アウトプットの初期化
	rvbstrValue.clear();

	// インプットのチェック
	if( 0 == bstrTagName.Length() )
	{
		return false;
	}

	HRESULT hResult;

	// タグ名のノードリストの取得
	IXMLDOMNodeListPtr pXMLDOMNodeList = NULL;
	hResult = pXMLDOMDocument->getElementsByTagName( bstrTagName, &pXMLDOMNodeList );
	if( FAILED(hResult) || NULL == pXMLDOMNodeList )
	{
		assert( !"タグ名のノードリストの取得に失敗" );
		return false;
	}
	// ノードリストのノードの数の取得
	long lCountNode = 0;
	hResult = pXMLDOMNodeList->get_length( &lCountNode );
	if( FAILED(hResult) )
	{
		assert( !"ノードリストのノードの数の取得に失敗" );
		return false;
	}
	// ノードリストのノード一つずつ処理
	for( int i = 0; i < lCountNode; ++i )
	{
		// ノードリストのうちの一つのノードの取得
		IXMLDOMNodePtr pXMLDOMNode = NULL;
		hResult = pXMLDOMNodeList->get_item( i, &pXMLDOMNode );
		if( FAILED(hResult) )
		{
			assert( !"ノードリストのうちの一つのノードの取得に失敗" );
			return false; 
		}

		// ノードタイプの取得
		DOMNodeType eNodeType;
		hResult = pXMLDOMNode->get_nodeType( &eNodeType );
		if( FAILED(hResult) )
		{
			assert( !"ノードタイプの取得に失敗" );
			return false; 
		}
		if( NODE_ELEMENT != eNodeType )
		{
			assert( !"ノードタイプがエレメントでないのはおかしい" );
			return false;
		}
		// エレメント型への変換
		IXMLDOMElementPtr pXMLDOMElement = NULL;
		hResult = pXMLDOMNode->QueryInterface( IID_IXMLDOMElement, (void**)&pXMLDOMElement );	// スマートポインタ型を利用しているので、pXMLDOMElement = pXMLDOMNode; でも良い。
		if( FAILED(hResult) || NULL == pXMLDOMElement )
		{
			assert( !"エレメント型への変換に失敗" );
			return false;
		}

		// 属性値の取得か、データ値の取得か
		if( bstrAttributeName.Length() )
		{	// 属性値の取得
			IXMLDOMAttribute* pAttributeNode = NULL;
			CComVariant varValue;
			hResult = pXMLDOMElement->getAttribute( bstrAttributeName, &varValue ); 
			if( SUCCEEDED(hResult) && VT_BSTR == varValue.vt )
			{
				rvbstrValue.push_back( varValue.bstrVal );
			}
		}
		else
		{	// データ値の取得
			CComBSTR bstrText;
			hResult = pXMLDOMElement->get_text( &bstrText );
			if( SUCCEEDED(hResult) )
			{
				rvbstrValue.push_back( bstrText );
			}
		}
	}

	return true;
}

bool ParseXmlFile( TCHAR* pszFileName )
{
	if( NULL == pszFileName )
	{
		return false;
	}

	HRESULT hResult;

	// DOMドキュメントの作成
	IXMLDOMDocumentPtr pXMLDOMDocument;
	hResult = pXMLDOMDocument.CreateInstance(CLSID_DOMDocument);
	if( FAILED(hResult) )
	{
		assert( !"DOMドキュメントの作成に失敗" );
		return false;
	}

	// XMLファイル読み込み
	pXMLDOMDocument->put_async( VARIANT_FALSE ); // load関数がロードを完了するまで待つようにする。
	VARIANT_BOOL varbResult;
	hResult = pXMLDOMDocument->load( CComVariant(pszFileName), &varbResult );
	if( FAILED(hResult) || !varbResult)
	{
		assert( !"XMLファイル読み込みに失敗" );
		return false;
	}

	std::vector	vbstrValue;

	// author要素の値の取得
	if( !GetXmlValue(	pXMLDOMDocument,
						_T("author"),
						_T(""),
						vbstrValue ) )
	{
		assert( !"author要素の値の取得に失敗" );
		return false;
	}
	std::cout << "author要素の値" << std::endl;
	for( unsigned int ui = 0; ui < vbstrValue.size(); ++ui )
	{
		std::cout << "\t" << _com_util::ConvertBSTRToString(vbstrValue[ui]) << std::endl;
	}

	// book要素のid属性の値の取得
	if( !GetXmlValue(	pXMLDOMDocument,
						_T("book"),
						_T("id"),
						vbstrValue ) )
	{
		assert( !"book要素のid属性の値の取得に失敗" );
		return false;
	}
	std::cout << "book要素のid属性の値" << std::endl;
	for( unsigned int ui = 0; ui < vbstrValue.size(); ++ui )
	{
		std::cout << "\t" << _com_util::ConvertBSTRToString((TCHAR*)vbstrValue[ui]) << std::endl;
	}

	return true;
}

int _tmain(int argc, _TCHAR* argv[])
{
	CoInitialize(NULL);	// COMの初期化処理

	ParseXmlFile( _T("books.xml") );

	CoUninitialize();	// COMの終了処理

	_getch();

	return 0;
}

---
以下を使用していたらスマートポインタに変える
・BSTR -> CComBSTR
・SysAllocString
・SysFreeString

[変更前]
LPOLESTR s
  BSTR in = SysAllocString(s);
  HRESULT h = foo->bar(in);
  SysFreeString(in);

[変更後]
  CComBSTR in = s;
  HRESULT h = foo->bar(in);

---
  HRESULT h = foo->bar(CComBSTR(s));