プログラミング データを塊で読み込む
あいさつ
どうも、はかせです。
前回読み込んだ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; }
確認用ということで、全表示のメソッドも用意しました。
実行結果です。
(多いので見切れてます)
値指定です。
sexタグの値がMaleのものを抽出しています。
ちなみに完全一致でなくても表示できます。
sexタグの先頭がFのものです。
ただし大文字小文字が違うと無理です。
あとがき
今回はxmlを塊で読んでみました。
データをそれぞれ構造体に格納しているので
検索や取り出しが楽です。
ただ、実際使うならstd::mapでなく、
決め打ちの構造体の方が楽かなという気がします。
(管理するデータの種類ってあらかじめ決めるだろうし)
それでは今回はこの辺でノシ