MSXML使ってみた2

間が開きすぎて何も身についていない感のあるMSXML
特定の属性値を持つ要素を取得して表示する処理を書いたんだけど、ずいぶん回り道をしているような感じが。もっとスッキリ書けそうな気がする。
今回も、ソースコードの利用は自己責任でお願いします。

XML

<?xml version="1.0" encoding="Shift-Jis" standalone="yes"?>
<member>
  <person gen="1">
    <name>Isono Namihei</name>
    <sex>Male</sex>
    <occupation>Office worker</occupation>
  </person>
  <person gen="1">
    <name>Isono Fune</name>
    <sex>Female</sex>
    <occupation>Homemaker</occupation>
  </person>
  <person gen="2">
    <name>Fuguta Sazae</name>
    <sex>Female</sex>
    <occupation>Homemaker</occupation>
  </person>
  <person gen="2">
    <name>Fuguta Masuo</name>
    <sex>Male</sex>
    <occupation>Office worker</occupation>
  </person>
  <person gen="2">
    <name>Isono Katsuo</name>
    <sex>Male</sex>
    <occupation>Pupil</occupation>
  </person>
  <person gen="2">
    <name>Isono Wakame</name>
    <sex>Female</sex>
    <occupation>Pupil</occupation>
  </person>
  <person gen="2">
    <name>Namino Norisuke</name>
    <sex>Male</sex>
    <occupation>Office worker</occupation>
  </person>
  <person gen="3">
    <name>Fuguta Tarao</name>
    <sex>Male</sex>
    <occupation>Child</occupation>
  </person>
  <person gen="3">
    <name>Namino Ikura</name>
    <sex>Male</sex>
    <occupation>Child</occupation>
  </person>
</member>

ソース

BSTR型やVariant型の取り扱いには、そのラッパークラスであるCComBSTRやCComVariantを使っています。そのためにatlbase.hをインクルードしてます。atlbase.hがない場合はWindows Driver Kitに入ってるんで、そちらをインストールしてください。

#import <msxml6.dll> named_guids raw_interfaces_only 
#include <atlbase.h>
#include <vector>
#include <iostream>

int ShowPersonsOfGenX(const WCHAR *pszGen);

int main()
{
	if ( FAILED(CoInitialize(NULL)) ) {
		return -1;
	}

	int nResult = ShowPersonsOfGenX(L"2");

	CoUninitialize();

	return nResult;
}

/* Gen="pszGen"となる人物を表示する関数 */
int ShowPersonsOfGenX(const WCHAR *pszGen)
{
	VARIANT_BOOL fSuccess;
	MSXML2::IXMLDOMDocument2Ptr pDoc;
	HRESULT		hResult = 0;
	pDoc.CreateInstance(__uuidof(MSXML2::DOMDocument60));
	std::vector<CComBSTR>	vecName;	// Gen="2"となる人物の名前を格納する

	//XMLファイルのロード
	pDoc->put_async(VARIANT_FALSE);				// 同期読み込みに設定
	pDoc->put_validateOnParse(VARIANT_FALSE);	// 妥当性確認しない
	hResult = pDoc->load(_variant_t("sazae.xml"), &fSuccess);
	if ( FAILED(hResult) || ( fSuccess != VARIANT_TRUE )) {
		return -1;
	}

	//personタグのノードリストを作成
	MSXML2::IXMLDOMNodeListPtr	pNodeListPerson = NULL;
	hResult = pDoc->getElementsByTagName((BSTR)L"person", &pNodeListPerson);
	if (FAILED(hResult) || (fSuccess != VARIANT_TRUE)) {
		return -1;
	}

	//ノードリストの長さを調べる
	long	lLen = 0;
	hResult = pNodeListPerson->get_length(&lLen);
	if ( FAILED(hResult) || lLen <= 0 ) {
		return -1;
	}

	for (int i = 0; i < lLen; i++) {
		//personタグの各ノードからgen属性を探す
		MSXML2::IXMLDOMNodePtr pNode = NULL;
		hResult = pNodeListPerson->get_item(i, &pNode);
		if (FAILED(hResult) || (NULL == pNode)) {
			continue;
		}

		MSXML2::IXMLDOMElementPtr pElem = NULL;
		hResult = pNode->QueryInterface(IID_IXMLDOMElement, (void **)&pElem);
		if ( FAILED(hResult) || ( NULL == pElem ) ) {
			continue;
		}

		CComVariant varValue;
		hResult = pElem->getAttribute((BSTR)L"gen", &varValue);
		if ( SUCCEEDED(hResult) && ( VT_BSTR == varValue.vt ) ) {
			//genの属性値が"2"かどうか調べる
			if ( varValue == CComVariant(pszGen)) {
				//nameタグに書かれている名前をvecNameに入れる

				//personタグの子ノードをまとめたノードリストを作成する
				MSXML2::IXMLDOMNodeListPtr pNodeListChild = NULL;
				hResult = pElem->get_childNodes(&pNodeListChild);
				if (FAILED(hResult) || (NULL == pNodeListChild)) {
					continue;
				}

				//ノードリストの長さを調べる
				long	lLenChild = 0;
				hResult = pNodeListChild->get_length(&lLenChild);
				if (FAILED(hResult) || ( lLenChild <= 0 )) {
					continue;
				}

				for (int j = 0; j < lLenChild; j++) {
					//nameタグのノードがあるかを調べる
					MSXML2::IXMLDOMNodePtr	pNodeChild = NULL;
					hResult = pNodeListChild->get_item(j, &pNodeChild);
					if (FAILED(hResult) || (NULL == pNodeChild)) {
						continue;
					}

					CComBSTR	bstrNode;
					hResult = pNodeChild->get_nodeName(&bstrNode);
					if (FAILED(hResult)) {
						continue;
					}

					if (CComBSTR(L"name") == bstrNode.m_str) {
						//名前を取得してvecNameに入れる
						CComBSTR	bstrName;
						hResult = pNodeChild->get_text(&bstrName);
						if (FAILED(hResult)) {
							continue;
						}
						vecName.push_back(bstrName);
					}

				}
			}
		}
	}

	/* 結果を表示 */
	std::vector<CComBSTR>::iterator it;
	std::wcout << L"Persons of Generation " << pszGen << L":" << std::endl;
	if ( vecName.empty() ) {
		std::wcout << L"There is no person who meets the condition." << std::endl;
	}
	else {
		for (it = vecName.begin(); it != vecName.end(); it++) {
			std::wcout << it->m_str << std::endl;
		}
	}

	return 0;
}