はかせのラボ

私の頭の中を書いていく雑記ブログです

プログラミング xmlのタグで検索する

あいさつ

どうも、はかせです。
インスタンシングが一段落ついたので、
今回からはxmlを使ったリソースの外部管理的なことをやってみます。

xml読み込み

xml読み込みに関しては以前の作品添削のフィードバックの際に
実装したことがあり、記事にしているのでそちらをご覧ください。
hakase0274.hatenablog.com

xmlタグ検索

xmlを使うということはデータにタグつけて、
読み込むことが目的であると思います。
ということでxmlの中から指定したタグのデータを取り出してみます。

細かい理屈の前にコードと読み込むデータです。

#pragma once
#include <string>
#include <winerror.h>
#include <vector>
#include <atlbase.h>
#include <iostream>

class XMLPerser
{
public:
	XMLPerser();
	~XMLPerser();
	HRESULT FileLoadWithTag(std::string inPath,std::wstring inTag);
};
#include "pch.h"
#include "XMLPerser.h"
#include<Shlwapi.h>

#pragma comment(lib, "Shlwapi.lib")
#import <msxml6.dll> named_guids raw_interfaces_only 

XMLPerser::XMLPerser()
{
	::CoInitialize(0);
}

XMLPerser::~XMLPerser()
{
	::CoUninitialize();
}

HRESULT XMLPerser::FileLoadWithTag(std::string inPath, std::wstring inTag)
{
	//loadの成否を格納
	VARIANT_BOOL fSuccess;
	//XMLを読み込むDOM
	MSXML2::IXMLDOMDocument2Ptr pDoc;
	//各関数の成否を格納
	HRESULT	hr = 0;
	//DOMインスタンスを作る
	pDoc.CreateInstance(__uuidof(MSXML2::DOMDocument60));
	//表示する要素を格納
	std::vector<CComBSTR> resultVector;	

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

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

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

	/*各personノード中から指定したTagのノードがあるか調べる*/
	for (int i = 0; i < lLen; i++) 
	{
		//personタグの要素を取得
		MSXML2::IXMLDOMNodePtr pNode = NULL;
		hr = pNodeListPerson->get_item(i, &pNode);
		if (FAILED(hr) || (NULL == pNode)) continue;

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

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

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

		/*指定タグのノードがあるかを調べる*/
		for (int j = 0; j < lLenChild; j++)
		{
			//子ノードの要素を取得
			MSXML2::IXMLDOMNodePtr	pNodeChild = NULL;
			hr = pNodeListChild->get_item(j, &pNodeChild);
			if (FAILED(hr) || (NULL == pNodeChild)) continue;
			//ノード名を取得
			CComBSTR bstrNode;
			hr = pNodeChild->get_nodeName(&bstrNode);
			if (FAILED(hr)) continue;
			//取得したノードが指定したタグのノードか
			if (CComBSTR(inTag.c_str()) != bstrNode.m_str) continue;

			CComBSTR bstrName;
			hr = pNodeChild->get_text(&bstrName);
			if (FAILED(hr)) continue;
			resultVector.push_back(bstrName);
		}		
	}

	/* 結果を表示 */
	std::vector<CComBSTR>::iterator it;
	std::wcout << L"SearchTag: " << inTag  << std::endl;
	if (resultVector.empty()) 
	{
		std::wcout << L"The Tag NotFound." << std::endl;
		return S_FALSE;
	}
	for (it = resultVector.begin(); it != resultVector.end(); it++)
	{
		std::wcout << it->m_str << std::endl;
	}
	return S_OK;
}
<?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>

コードは長いですしやたらチェックが入っていますが、
やっていることは単純です。
XMLファイルを読み込む
②personという大きいタグ単位でデータを取得
③各person毎にノード検索を行い指定タグのノードを取得

①は以前やったやつなので割愛します。
②③は下記のコードで行います。

MSXML2::IXMLDOMNodeListPtr	pNodeListPerson = NULL;
hr = pDoc->getElementsByTagName((BSTR)L"タグ", &pNodeListPerson);

IXMLDOM~PtrはMSXMLxmlデータを扱う型になります。
Ptrが付くものと付かないものがありますが、
Ptr付はスマートポインタと同種のものなので、
基本はPtr付を使えばいいと思います。

getElementsByTagNameは名前の通りタグで情報を取得する関数です。
BSTR型にワイド文字をキャストすることで使えます。
(普通のunicode文字だとうまくいきませんでした)

では実行です。
f:id:hakase0274:20190622233049p:plain

タグが存在する場合はそのタグの情報が表示され、
存在しない場合はNotFoundと出ています。

あとがき

今回はxmlデータのタグ検索でした。
基本的に環境構築とかができていれば
MSXMLAPI叩くだけなので楽と言えば楽ですね。

それでは今回はこの辺でノシ