C++BuilderでMSXML6を使用する
C++Builder標準のTXMLDocumentはXPathが使えないなど微妙に使いづらいので、MSXMLのタイプライブラリをインポートして使ってみる。
まず、以下の手順でMSXMLのタイプライブラリをインポートする。
- [コンポーネント|コンポーネントのインポート]を選択
- "VCL for C++ Win32"を選択して[次へ]をクリック
- タイプライブラリの取り込み
- "Microsoft XML, v6.0"を選択して[追加]をクリック *1
- タイプライブラリの出力先を入力。デフォルトは、マイドキュメント\RAD Studio\7.0\Imports\*2
- [次へ]をクリック
- ユニットの作成を選択して[完了]をクリック
- 出力先にMSXML2_TLB.cpp、MSXML2_TLB.h、MSXML2_OCX.dcrが生成される
実際に使う場合は、プロジェクトに追加するだけでOK。
- [プロジェクト|オプション]を選択
- インクルードパスにタイプライブラリの出力先を追加
- MSXML2_TLB.hをインクルード
- プロジェクトに$(BDSUSERDIR)\Imports\MSXML2_TLB.cppを追加
XMLファイルをパースするコードは以下の通り。
void __fastcall TForm1::Button1Click(TObject *Sender) { HRESULT hr; IXMLDOMDocumentPtr pDoc; hr = pDoc.CreateInstance(CLSID_DOMDocument); _ASSERTE(hr); pDoc->async = VARIANT_FALSE; // XMLファイルのロード short r; hr = pDoc->load(Variant("TEST1.XML"), &r); if(hr != S_OK || !r){ // エラー処理 ShowMessage(_T("LOAD ERROR")); return; } ShowMessage(pDoc->documentElement->tagName); }
XMLファイルを保存する場合はIXMLDOMDocument::saveでもいいのだけれど、インデントしてくれないのでIMXWriterを使って保存する。
bool SaveXML(IXMLDOMDocumentPtr pDoc, UnicodeString strPath) { TFileStream* pFS = new TFileStream(strPath, fmCreate); TStreamAdapter* pSA = new TStreamAdapter(pFS, soOwned); // TStreamAdapterはTStreamとIStreamの仲介役 HRESULT hr; IMXWriterPtr pWriter; hr = pWriter.CreateInstance(CLSID_msMXXMLWriter); _ASSERTE(hr == S_OK); pWriter->version = L"1.0"; pWriter->encoding = L"UTF-8"; // 文字コードを指定 pWriter->indent = VARIANT_TRUE; // TRUEでは駄目 IStream* pIS = *pSA; // TFileStreamに付加したIStreamインターフェースを取得 pWriter->set_output(Variant(pIS)); // XMLの出力先をTFileStreamにする ISAXXMLReaderPtr pReader; pReader.CreateInstance(CLSID_msSAXXMLReader); hr = pReader->putContentHandler((ISAXContentHandlerPtr)pWriter); _ASSERTE(hr == S_OK); hr = pReader->parse(Variant((IUnknown*)pDoc)); _ASSERTE(hr == S_OK); return true; }
ファイルの保存はTFileStreamを使用する。IMXWriterは出力先にIStreamインターフェースが必要なので、TStreamにIStreamインターフェースを付加してくれるTStreamAdapterを使用する。
TStreamAdapterはアプリケーションで明示的に破棄する必要はなく、TFileStreamもsoOwnedでTStreamAdapterと結びつけているので、TFileStreamの破棄も不要。*3
COMオブジェクトに渡す論理型(VARIANT_BOOL型)は、windef.hで定義されているTRUE/FALSEと違うので要注意。*4
*2:プロジェクト内では$(BDSUSERDIR)\Imports\で参照できる。
*3:http://docwiki.embarcadero.com/VCL/ja/Classes.TStreamAdapter.Destroy