[an error occurred while processing this directive] [an error occurred while processing this directive]

MSXML

Microsoftが提供するXML用のライブラリ。MicrosoftもXMLの普及には積極的で、これまで何度もバージョンアップを繰り返しています…てかそれが曲者。バージョンごとに色々仕様が違うらしい…あぁ…
参考にさせていただいたサイトです。ありがとうございました。

基本的なイメージ

XMLはツリー構造と考えることが出来ます。
<root>
<student>
<name>Kei</name>
<age>22</age>
</student>
</root>
なら、rootというノードからstudentというノードに枝が生えていて、studentからnameとageというノードに枝が伸びています。ノードによっては値が付いています。データの読み出し・書き出しもこのノード単位で行います。
上の構造を書き出すコードの概略は以下の通り。 読み出しはこの逆で、オープンしたファイルに対して順番にノードをたどって読み出していきます。

ファイルオープンの決まり事

MSXMLはCOMと呼ばれるライブラリです。これはDLLの一種で、JavaScriptとかから呼び出せたり気軽に使えるみたいです。VC++では#includeでヘッダファイルを読み込むのではなくて、#import文でdllを直接呼び出します。でもいくつか決まり事に従う必要があります。
今回関係するのは、COMオブジェクトを使用する前には、それを用いるスレッドで
  CoInitialize(NULL);
を呼ぶ必要がある点です。これを行わないと、createInstanceでインスタンスを作れない(NULLのまま)で死亡します。
あと、importに関してはVisualStudio.NET 2003では色々混乱があるようです。詳しくはこちらを参照願いたいですが、結局の所ネームスペース名(MSXML)が問題を起こすようです。僕は全部のクラスにMSXML::というネームスペース指定を書いて回避しました。

結局のところ、XMLファイルをオープンするにはこんなコードになります。
#import "msxml.dll"
// msxml2.dll / msxml3.dllを用いた場合は、以下のMSXML::というネームスペース指定が
// MSXML2::に変わります

// 以下のコードを、save_to_xmlを呼び出す前に呼び出す
//  CoInitialize(NULL);

void save_to_xml(std::string file, std::vector tis){
  MSXML::IXMLDOMDocumentPtr doc("MSXML.DOMDocument");
// または
// MSXML::IXMLDOMDocumentPtr doc;
// doc.CreateInstance("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]