XMLはツリー構造と考えることが出来ます。
<root>
<student>
<name>Kei</name>
<age>22</age>
</student>
</root>
なら、rootというノードからstudentというノードに枝が生えていて、studentからnameとageというノードに枝が伸びています。ノードによっては値が付いています。データの読み出し・書き出しもこのノード単位で行います。
上の構造を書き出すコードの概略は以下の通り。
- ファイル開く
- rootノードを作る
- nameノードを作ってrootにつなげる
- ageノードを作ってrootにつなげる
- セーブする
読み出しはこの逆で、オープンしたファイルに対して順番にノードをたどって読み出していきます。
MSXMLはCOMと呼ばれるライブラリです。これはDLLの一種で、JavaScriptとかから呼び出せたり気軽に使えるみたいです。VC++では#includeでヘッダファイルを読み込むのではなくて、#import文でdllを直接呼び出します。でもいくつか決まり事に従う必要があります。
今回関係するのは、COMオブジェクトを使用する前には、それを用いるスレッドで
CoInitialize(NULL);
を呼ぶ必要がある点です。これを行わないと、createInstanceでインスタンスを作れない(NULLのまま)で死亡します。
あと、importに関してはVisualStudio.NET 2003では色々混乱があるようです。詳しくは
こちらを参照願いたいですが、結局の所ネームスペース名(MSXML)が問題を起こすようです。僕は全部のクラスにMSXML::というネームスペース指定を書いて回避しました。
結局のところ、XMLファイルをオープンするにはこんなコードになります。
#import "msxml.dll"
void save_to_xml(std::string file, std::vector tis){
MSXML::IXMLDOMDocumentPtr doc("MSXML.DOMDocument");
...書き込み処理とか読み込み処理とか
とりあえず、ここで対象にするデータはこんなの。
<?xml version="1.0" encoding="ascii" ?>
<root>
<TextItem>
<title<yahoo>/title>
<description>hogehoge</description>
</TextItem>
</root>
文字コードはasciiって書いてあるけど、日本語使うならshift_jisとかutf-8とか指定します。
まずはTextItemを定義しました。
#pragma once
#include <vector>
#include <string>
class TextItem {
public:
std::string title;
std::string description;
TextItem(){}
TextItem(std::string p_title, std::string p_description)
: title(p_title), description(p_description) {}
void save(MSXML::IXMLDOMNodePtr node) const {
MSXML::IXMLDOMDocumentPtr doc = node->ownerDocument;
MSXML::IXMLDOMNodePtr elem = doc->createElement("TextItem");
MSXML::IXMLDOMNodePtr item;
MSXML::IXMLDOMTextPtr text;
// title
item = doc->createElement("title");
text = doc->createTextNode(title.c_str());
item->appendChild(text);
elem->appendChild(item);
// text
item = doc->createElement("description");
text = doc->createTextNode(description.c_str());
item->appendChild(text);
elem->appendChild(item);
node->appendChild(elem);
}
void load(MSXML::IXMLDOMNodePtr node) {
assert(node->nodeName == _bstr_t("TextItem"));
MSXML::IXMLDOMNodePtr item;
MSXML::IXMLDOMNodePtr text;
item = node->firstChild;
text = item->firstChild;
title = static_cast<const char*>(_bstr_t(text->nodeValue));
item = item->nextSibling;
text = item->firstChild;
description = static_cast<const char*>(_bstr_t(text->nodeValue));
}
};
このあと、TextItemの配列を引数に取って、データをXMLに読み書きするsave_text_itemsとload_text_itemsを書きました。
void save_text_items(std::string& file, std::vector<TextItem *>& tis){
CoInitialize(NULL);
MSXML::IXMLDOMDocumentPtr doc("MSXML.DOMDocument");
doc->appendChild(doc->createProcessingInstruction(
"xml", "version='1.0' encoding='ascii'"));
MSXML::IXMLDOMNodePtr root = doc->createElement("root");
doc->appendChild(root);
for ( int i = 0; i < (int)tis.size(); ++i ){
tis[i]->save(root);
}
doc->save(file.c_str());
}
void load_text_items(std::string& file, std::vector<TextItem *>& tis) {
CoInitialize(NULL);
MSXML::IXMLDOMDocumentPtr doc("MSXML.DOMDocument");
doc.CreateInstance(CLSID_DOMDocument);
doc->validateOnParse = VARIANT_FALSE;
doc->load(file.c_str());
MSXML::IXMLDOMNodePtr root = doc->documentElement;
for(MSXML::IXMLDOMNodePtr node = root->firstChild; node != NULL; node = node->nextSibling ) {
TextItem * ti = new TextItem();
ti->load(node);
tis.push_back(ti);
}
}
docに対して.アクセスと->アクセスが混在して非常に怪しいですが、これでOKです。
[an error occurred while processing this directive]