Astonish Arts  > AADS  > MFC  > MSXML

Astonish Arts Developer Section

MFC

MSXML

このページでは MSXML を使って、XML を扱う方法を解説します。
このページの内容は Windows Vista SP1 + Vitual Studio 2005 でプログラムを作成しています。
MSXMLのバージョンは3.0を使用します。
>MSXML を使うための準備
>XMLファイルを読み込む
>XML内のデータ(タグ内テキスト,アトリビュート(属性))の読み込み
>XMLファイルを保存
>XML内のデータ(タグ内テキスト,アトリビュート(属性))の書き込み
>インデントをつけてXMLを保存

MSXML を使うための準備

MSXMLを使うための準備について解説をします。
MSXMLを扱う場合にはいくつかの手順を踏む必要があります。
まずはじめに、MSXMLライブラリをインポートします。
以下のコードをファイルの先頭あたりに追加します。
このコードを追加することでプロジェクトのビルドのタイミングでDebugビルドならDebugフォルダ内(ReleaseビルドならReleaseフォルダ内)に「msxml3.tli」、「msxml3.tlh」が作成されます。
// +--- ライブラリをインポートする。
#include "msxml.h"
#import <msxml3.dll>
using namespace MSXML2;
			
COM の初期化を行います。
CoInitialize()を呼び出せば初期化は行われますが、その場合CoInitialize()呼び出した回数分CoUninitialize()をしなければいけません。
なので、CXXXApp::InitInstanceでAfxOleInit()を1度だけ呼び出します。
AfxOleInit()は、内部でCoInitialize()が呼ばれ、アプリケーション終了時に勝手にCoUninitialize()が呼ばれるようになります。
// +--- COM の初期化
AfxOleInit()
			
次に日本語を扱うためのロケーションを設定します。
この処理をしないと、文字化けをしてしまいます。
このコードは任意の場所で構いません。(例えばCXXXApp::InitInstance内)
#include <locale.h>
...
// +--- 言語の指定
setlocale(LC_ALL, "Japanese");
			

XMLファイルを読み込む

MSXMLを使ってXMLファイルを読み込む方法を解説します。
読み込みに関してサンプルとして以下のXMLを使います。
Visual Studio のプロジェクトファイル(xxx.vcproj)はXMLで書かれていますので、そちらの一部を使用します。
<?xml version="1.0" encoding="shift_jis"?>
<VisualStudioProject
    ProjectType="Visual C++"
    Version="8.00"
    Name="XmlTest"
    ProjectGUID="{AAAAAAAA-BBBB-CCCC-DDDD-EEEEEEEEEEEE}"
    RootNamespace="XmlTest"
    Keyword="MFCProj"
    >
    <Platforms>
        <Platform Name="アトリビュート1(属性)">
            ここがタグ内テキスト1
        </Platform>
        <Platform Name="アトリビュート2(属性)">
            ここがタグ内テキスト2
        </Platform>
        <Platform Name="アトリビュート3(属性)">
            ここがタグ内テキスト3
        </Platform>
    </Platforms>
</VisualStudioProject>
			
まずはじめに、IXMLDOMDocument2の実体を作成し、指定のファイルを読み込みます。
// +--- MSXML2::IXMLDOMDocument2Ptr の実体を作成する
MSXML2::IXMLDOMDocument2Ptr pXmlDoc;
// +--- 実体の作成時は「->」ではなく「.」を使うことに注意。
pXmlDoc.CreateInstance( __uuidof( MSXML2::DOMDocument30 ) );
pXmlDoc->async = VARIANT_FALSE;
// +--- XMLファイルを読み込む。
pXmlDoc->load( L"Text.xml" );
			

XML内のデータの読み込み(タグ内テキスト,アトリビュート(属性))

タグ内のテキストとは上のXMLのサンプルに書かれている「タグ内テキスト」の様な位置にあるデータのことを示します。
アトリビュート(属性)とは上のXMLのサンプルに書かれている「アトリビュートxx(属性)」の位置にあるデータのことを示します。
以下のコードは、そのデータを取得するためのコードになります。
// +--- ノードトリスト(Platform)を取得します。
MSXML2::IXMLDOMNodeListPtr	pNodeList = pXmlDoc->getElementsByTagName(
    _bstr_t( L"Platform" )
);

// +--- Platform ノードをすべて取り出す
// +--- 
for ( int i = 0; i < pNodeList->Getlength(); i++ ){

    // +--- 先頭のものから順番にエレメントを取り出す
    MSXML2::IXMLDOMElementPtr    pElem = Getitem( i );
    
    // +--- タグ内のテキストを取り出す
    _variant_t vInnerText = pElem->Gettext();
    CString    strInnerText = vInnerText.bstrVal;
    
    OutputDebugString( strInnerText );
    
    // +--- エレメントからアトリビュート(属性)を取り出す
    MSXML2::IXMLDOMAttributePtr  pAttr = pElem->getAttributeNode(
        _bstr_t( L"Name" )
    );
    // +--- 属性のデータを取得する
    _variant_t vAttr = pAttr->Gettext();
    CString    strAttr = vAttr.bstrVal;
    
    OutputDebugString( strAttr );
}

// +--- 上のコードを実行すると以下のように表示されます。
//ここがタグ内テキスト1
//アトリビュート1(属性)
//ここがタグ内テキスト2
//アトリビュート2(属性)
//ここがタグ内テキスト3
//アトリビュート3(属性)
			
_variant_t型は、様々なタイプの型(int, unsigned intやcharなど)を内包したマルチな型です、
ここの例ではCStringに変換するために.bstrValを使っていますが、整数の場合は.intValなど様々なデータに対応ができるようです。

XMLファイルを保存

書き込みたいXMLのドキュメントを作成します。
// +--- MSXML2::IXMLDOMDocument2Ptr の実体を作成する
MSXML2::IXMLDOMDocument2Ptr pXmlDoc;
// +--- 実体の作成時は「->」ではなく「.」を使うことに注意。
pXmlDoc.CreateInstance( __uuidof( MSXML2::DOMDocument30 ) );
pXmlDoc->async = VARIANT_FALSE;

// +--- XMLのヘッダ部分を作成する
// +--- 以下の場合だとxmlのバージョンは1.0、文字コードはShift_JIS
pXmlDoc->appendChild(
    pXmlDoc->createProcessingInstruction(
        L"xml", L"version=\'1.0\' encoding=\'Shift_JIS\'"
    )
);

// +--- xmlの内容の作成ここから
....①
// +--- xmlの内容の作成ここまで

// +--- xmlファイルを保存する。
pXmlDoc->save( _bstr_t( L"Test.xml" ) );
			
以上でXMLヘッダつきの空のXMLファイルが作成されます。
①の位置に最適なXMLの内容を追加するコードを書くことで、希望に沿った形のXMLを作成することができます。

XML内のデータの書き込み(タグ内テキスト,アトリビュート(属性))

書き込みたいデータ(タグ内テキスト,アトリビュート(属性))をXMLの要素に追加します。
「XMLファイルを保存」で解説したソースコードの、①の位置にコードを追加します。
サンプルとして以下のようなXMLを作成します。
これは、読み込みのサンプルとして使っているものと同じものです。
<?xml version="1.0" encoding="shift_jis"?>
<VisualStudioProject
    ProjectType="Visual C++"
    Version="8.00"
    Name="XmlTest"
    ProjectGUID="{AAAAAAAA-BBBB-CCCC-DDDD-EEEEEEEEEEEE}"
    RootNamespace="XmlTest"
    Keyword="MFCProj"
    >
    <Platforms>
        <Platform Name="アトリビュート1(属性)">
            ここがタグ内テキスト1
        </Platform>
        <Platform Name="アトリビュート2(属性)">
            ここがタグ内テキスト2
        </Platform>
        <Platform Name="アトリビュート3(属性)">
            ここがタグ内テキスト3
        </Platform>
    </Platforms>
</VisualStudioProject>
			
以下のコードを追加することで、同じ階層のXMLが作成されます。
ただし、このままのコードではインデントはとられていません。
// +--- Project ノード の作成
MSXML2::IXMLDOMNodePtr	pProject = pXmlDoc->createElement(
    L"VisualStudioProject"
);


// +--- Project Type アトリビュートを追加する
MSXML2::IXMLDOMAttributePtr	pProjType = pXmlDoc->createAttribute(
    L"ProjectType"
);

pProjType->text = L"Visual C++";
// +--- Purojectノードに追加する
pProject->attributes->setNamedItem( pProjType );

// +--- Version アトリビュートを追加する
MSXML2::IXMLDOMAttributePtr	pVersion = pXmlDoc->createAttribute(
    L"Version"
);
pVersion->text = L"8.00";
// +--- Purojectノードに追加する
pProject->attributes->setNamedItem( pVersion );

// +--- Name アトリビュートを追加する
MSXML2::IXMLDOMAttributePtr	pName = pXmlDoc->createAttribute(
    L"Version"
);
pName->text = L"XmlTest";
// +--- Purojectノードに追加する
pProject->attributes->setNamedItem( pName );

// +--- ProjectGUID アトリビュートを追加する
MSXML2::IXMLDOMAttributePtr	pProjectGUID = pXmlDoc->createAttribute(
    L"ProjectGUID"
);
pProjectGUID->text = L"{AAAAAAAA-BBBB-CCCC-DDDD-EEEEEEEEEEEE}";
// +--- Purojectノードに追加する
pProject->attributes->setNamedItem( pProjectGUID );

// +--- RootNamespace アトリビュートを追加する
MSXML2::IXMLDOMAttributePtr	pRootNamespace = pXmlDoc->createAttribute(
    L"RootNamespace"
);
pRootNamespace->text = L"XmlTest";
// +--- Purojectノードに追加する
pProject->attributes->setNamedItem( pRootNamespace );

// +--- Keyword アトリビュートを追加する
MSXML2::IXMLDOMAttributePtr	pKeyword = pXmlDoc->createAttribute(
    L"Keyword"
);
pKeyword->text = L"MFCProj";
// +--- Purojectノードに追加する
pProject->attributes->setNamedItem( pKeyword );


// +--- Platforms ノードを作成する
MSXML2::IXMLDOMNodePtr	pPlatforms = pXmlDoc->createElement(
    L"Platforms"
);


// +--- Platform1 ノードを作成する
// +--- 実際使う場合はfor文などで指定の回数回すほうがいい

// +--- Platformノードを追加する
MSXML2::IXMLDOMNodePtr pPlatform1 = pXmlDoc->createElement( L"Platform" );
// +--- タグ内テキストを追加する
pPlatform1->text = L"ここがタグ内テキスト1";
// +--- アトリビュートを追加する
MSXML2::IXMLDOMAttributePtr pPf1Attr = pXmlDoc->createAttribute( L"Name" );
pPf1Attr->text = L"アトリビュート1(属性)";
pPlatform1->attributes->setNamedItem( pPf1Attr );

// +--- Platformノードを追加する
MSXML2::IXMLDOMNodePtr pPlatform2 = pXmlDoc->createElement( L"Platform" );
// +--- タグ内テキストを追加する
pPlatform2->text = L"ここがタグ内テキスト2";
// +--- アトリビュートを追加する
MSXML2::IXMLDOMAttributePtr pPf2Attr = pXmlDoc->createAttribute( L"Name" );
pPf2Attr->text = L"アトリビュート2(属性)";
pPlatform2->attributes->setNamedItem( pPf2Attr );

// +--- Platformノードを追加する
MSXML2::IXMLDOMNodePtr pPlatform3 = pXmlDoc->createElement( L"Platform" );
// +--- タグ内テキストを追加する
pPlatform3->text = L"ここがタグ内テキスト3";
// +--- アトリビュートを追加する
MSXML2::IXMLDOMAttributePtr pPf3Attr = pXmlDoc->createAttribute( L"Name" );
pPf3Attr->text = L"アトリビュート3(属性)";
pPlatform3->attributes->setNamedItem( pPf3Attr );


// +--- ノードの階層を作っていく
pPlatforms->appendChild( pPlatform1 );
pPlatforms->appendChild( pPlatform2 );
pPlatforms->appendChild( pPlatform3 );

pProject->appendChild( pPlatforms );

pXmlDoc->appendChild( pProject );
			
以上でXMLヘッダつきの空のXMLファイルが作成されます。
①の位置に最適なXMLの内容を追加するコードを書くことで、希望に沿った形のXMLを作成することができます。

インデントをつけてXMLを保存

XMLをインデントをつけて保存する方法を解説します。
通常の保存方法(pXmlDoc->save( _bstr_t( L"Test.xml" ) );)を使ってXMLを保存した場合、インデントはとられない1行の文字列として保存されます。
以下のコードを作成することで、インデントをとられた形での保存が可能です。
なお、MSXMLのバージョン3.0の場合、文字コードを「Shift_JIS」に指定した時、ファイルの内容が長い(特殊な文字に反応してるかもしれない)場合にはエラーがでて保存に失敗します。
そのため、文字コードには「UTF-8」を使用します。
バージョン4.0では「Shift_JIS」でも問題なく動作することを確認しました。
IStreamPtr	smFile;

HRESULT hRes = SHCreateStreamOnFile(
    strFilePath,
    STGM_READWRITE | STGM_SHARE_DENY_WRITE | STGM_CREATE,
    &smFile
);

// +--- ファイルの作成に失敗した
if ( FAILED( hRes ) )
{
    // +--- ファイルの作成失敗
    return FALSE;
}

// +--- ライターの作成
IMXWriterPtr pWriter;
pWriter.CreateInstance( __uuidof( MSXML2::MXXMLWriter30 ) );

// +--- ヘッダ情報をセットする
pWriter->put_version( L"1.0" );
pWriter->put_encoding( L"UTF-8" );
// +--- インデントを有効に
pWriter->put_indent( VARIANT_TRUE );
pWriter->put_output( _variant_t( (IUnknown*)(IUnknownPtr)smFile ) );

// +--- リーダーの作成
ISAXXMLReaderPtr	pReader;
pReader.CreateInstance( __uuidof( MSXML2::SAXXMLReader30 ) );

pReader->putContentHandler( (MSXML2::ISAXContentHandlerPtr)pWriter );
pReader->parse( _variant_t( (IUnknown*)pDoc.GetInterfacePtr() ) );
			
以上のコードでインデントをとってXMLファイルを保存します。
このコードを使う場合は、pXmlDoc->save( _bstr_t( L"Test.xml" ) )などのドキュメントから保存するコードは削除してください。
コピーライト