はかせのラボ

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

プログラミング データを塊で読み込む

あいさつ

どうも、はかせです。
前回読み込んだxmlファイルの中からタグで検索をしました。
今回はデータを塊で読み込み、その中からタグ検索をしてみます。

やること

やることは極めて単純です。
・データを大きいタグごとの塊として配列に格納する
これだけです。

ではやっていきましょう。
前回同様まずはコードとデータです。

#include <atlbase.h>
#include <iostream>
#include <map>

struct PersonData
{
	std::map<CComBSTR, CComBSTR> NodeData;
};

class XMLPerser
{
public:
	XMLPerser();
	~XMLPerser();
	HRESULT FileLoad(std::string inPath);
	void ShowXMLData();
	void ShowXMLData(std::wstring inTag, std::wstring inValue);
private:
	std::vector<std::unique_ptr<PersonData>> mPersonDatas;
};
#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::FileLoad(std::string inPath)
{
	//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;

		std::unique_ptr<PersonData> data = std::make_unique<PersonData>();
		/*指定タグのノードがあるかを調べる*/
		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;
			//取得したノードが指定したタグのノードか

			CComBSTR bstrName;
			hr = pNodeChild->get_text(&bstrName);
			if (FAILED(hr)) continue;

			data->NodeData.emplace(bstrNode.m_str, bstrName);
		}
		mPersonDatas.push_back(std::move(data));
	}
	return S_OK;
}

void XMLPerser::ShowXMLData()
{
	for (auto &data : mPersonDatas)
	{
		std::wcout << L"Name:" << data->NodeData["name"].m_str << std::endl;
		std::wcout << L"Sex:" << data->NodeData["sex"].m_str << std::endl;
		std::wcout << L"Occupation:" << data->NodeData["occupation"].m_str << std::endl;
		std::wcout << std::endl;
	}
}

void XMLPerser::ShowXMLData(std::wstring inTag, std::wstring inValue)
{
	bool isFound = false;
	for (auto &data : mPersonDatas)
	{
		if (*data->NodeData[CComBSTR(inTag.c_str())].m_str != *CComBSTR(inValue.c_str()).m_str) continue;
		isFound = true;
		std::wcout << L"Name:" << data->NodeData["name"].m_str << std::endl;
		std::wcout << L"Sex:" << data->NodeData["sex"].m_str << std::endl;
		std::wcout << L"Occupation:" << data->NodeData["occupation"].m_str << std::endl;
		std::wcout << std::endl;
	}
	if (!isFound) std::wcout << "Not Found" << std::endl;
}

読み込むデータはいつもどおりのやつです。

<?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>

今回はpersonタグを一つの塊として読み込みます。
汎用ライブラリでも作ろうとしない限り、
読み込むデータなどは決め打ちでいいと思うのですが、
今回は勉強ということでstd::mapを使って汎用的に読んでみます。

struct PersonData
{
	std::map<CComBSTR, CComBSTR> NodeData;
};

あとは割と前回と同じです。
前回は指定したタグ以外は弾いていたんですが、
今回はタグをキーとしてmapに入れます。

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;

	std::unique_ptr<PersonData> data = std::make_unique<PersonData>();
	/*指定タグのノードがあるかを調べる*/
	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;
		//取得したノードが指定したタグのノードか

		CComBSTR bstrName;
		hr = pNodeChild->get_text(&bstrName);
		if (FAILED(hr)) continue;

		data->NodeData.emplace(bstrNode.m_str, bstrName);
	}
	mPersonDatas.push_back(std::move(data));
}

これでxmlの読み込みとデータの格納が終わりました。
あとはタグとその値を指定し、該当するデータを表示するだけです。

void XMLPerser::ShowXMLData()
{
	for (auto &data : mPersonDatas)
	{
		std::wcout << L"Name:" << data->NodeData["name"].m_str << std::endl;
		std::wcout << L"Sex:" << data->NodeData["sex"].m_str << std::endl;
		std::wcout << L"Occupation:" << data->NodeData["occupation"].m_str << std::endl;
		std::wcout << std::endl;
	}
}

void XMLPerser::ShowXMLData(std::wstring inTag, std::wstring inValue)
{
	bool isFound = false;
	for (auto &data : mPersonDatas)
	{
		if (*data->NodeData[CComBSTR(inTag.c_str())].m_str != *CComBSTR(inValue.c_str()).m_str) continue;
		isFound = true;
		std::wcout << L"Name:" << data->NodeData["name"].m_str << std::endl;
		std::wcout << L"Sex:" << data->NodeData["sex"].m_str << std::endl;
		std::wcout << L"Occupation:" << data->NodeData["occupation"].m_str << std::endl;
		std::wcout << std::endl;
	}
	if (!isFound) std::wcout << "Not Found" << std::endl;
}

確認用ということで、全表示のメソッドも用意しました。
実行結果です。
f:id:hakase0274:20190623233110p:plain
(多いので見切れてます)

値指定です。
sexタグの値がMaleのものを抽出しています。
f:id:hakase0274:20190623233220p:plain

ちなみに完全一致でなくても表示できます。
sexタグの先頭がFのものです。
f:id:hakase0274:20190623233337p:plain

ただし大文字小文字が違うと無理です。
f:id:hakase0274:20190623233423p:plain

あとがき

今回はxmlを塊で読んでみました。
データをそれぞれ構造体に格納しているので
検索や取り出しが楽です。

ただ、実際使うならstd::mapでなく、
決め打ちの構造体の方が楽かなという気がします。
(管理するデータの種類ってあらかじめ決めるだろうし)

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