2007-11-21

XMLの読み込み

アプリの設定ファイルを XMLで書いてみたくて XMLの読み込みについて調べてみました。
ちなみに読み込みたいXMLはこんな感じです。(最初なので簡単なところから...)
<sample>
    <config name="A" type="1" />
    <config name="B" type="2" />
    <config name="C" type="3" />
</sample>
 
SDKのMailSetサンプルでXMLを読んでいる部分があったので参考にしてみます。
(と言うよりほとんどそのままです)
 
まずは サンプルで多様しているオブジェクト開放のマクロを拝借。
#define RELEASE_OBJ(s)  \
    if (s != NULL)      \
    {                   \
        s->Release();   \
        s = NULL;       \
    }
 
続いてXMLの読み込み部分。
わかりにくくなるのでエラー処理をバッサリ切り捨てたらこんな感じになりました。
#define XML_FILE_NAME           _T("\\sample.xml")
#define ROOT_ELEMENT_NAME       _T("sample")
#define CONFIG_ELEMENT_NAME     _T("config")
#define NAME_ATTRIBUTE_NAME     _T("name")
#define TYPE_ATTRIBUTE_NAME     _T("type")

// XMLファイルの読み込み
static HRESULT LoadXML()
{
    HRESULT             hr              = S_OK;
    IXMLDOMDocument     *pDom           = NULL;
    IXMLDOMNode         *pTopNode       = NULL,
                        *pConfigNode    = NULL,
                        *pSibling       = NULL;
    IXMLDOMNamedNodeMap *pNodeMap       = NULL;
    CLSID               clsid           = {0};
    VARIANT             vt              = {0};
    VARIANT_BOOL        vbSuccess       = 0;
    BSTR                bstrName        = NULL;
    PTCHAR              pszName         = NULL,
                        pszType         = NULL;

    TCHAR               szMessage[256]  = _T("");

    // COMの初期化
    CoInitializeEx(NULL, 0);

    // XML DOM読み込み
    hr = CLSIDFromProgID(_T("Msxml2.DOMDocument"), &clsid);
    hr = CoCreateInstance(clsid, NULL, CLSCTX_INPROC_SERVER, IID_IXMLDOMDocument, (LPVOID *) &pDom);

    // XMLファイル読み込み
    vt.vt       = VT_BSTR;
    vt.bstrVal  = SysAllocString(XML_FILE_NAME);
    hr = pDom->load(vt, &vbSuccess);

    // ルート要素を取得
    hr = pDom->selectSingleNode(ROOT_ELEMENT_NAME, &pTopNode);
    // ルートノードの子要素の集合を取得
    hr = pTopNode->get_firstChild(&pConfigNode);
    while (pConfigNode) {
        // 子要素の名前を取得
        hr = pConfigNode->get_nodeName(&bstrName);
        if (!lstrcmpi(bstrName, CONFIG_ELEMENT_NAME)) {
            // <config>要素の場合は "name"属性と "type"属性の値を取得
            RELEASE_OBJ(pNodeMap);
            hr = pConfigNode->get_attributes(&pNodeMap);
            pszName = ReadNodeString(pNodeMap, NAME_ATTRIBUTE_NAME);
            pszType = ReadNodeString(pNodeMap, TYPE_ATTRIBUTE_NAME);
            //
            wsprintf(szMessage, _T("name=%s : type=%s"), pszName, pszType);
            MessageBox(NULL, szMessage, _T("XML読み込みテスト"), MB_OK);
            free(pszName);
            free(pszType);
            //
        }
        SysFreeString(bstrName);
        // 次の要素を選択
        hr = pConfigNode->get_nextSibling(&pSibling);
        pConfigNode->Release();
        pConfigNode = pSibling;
    }
    
    // 後始末系
    if (pszName) free(pszName);
    if (pszType) free(pszType);
    
    RELEASE_OBJ(pSibling);
    RELEASE_OBJ(pConfigNode);
    RELEASE_OBJ(pTopNode);
    RELEASE_OBJ(pNodeMap);
    RELEASE_OBJ(pDom);
    
    VariantClear(&vt);
    CoUninitialize();
    
    return hr;
}

// 指定された属性の値を読み込む
static LPTSTR ReadNodeString(IXMLDOMNamedNodeMap *pNodeMap, TCHAR *pszNodeName)
{
    HRESULT     hr          = S_OK;
    IXMLDOMNode *pAttlibute = NULL;
    VARIANT     vt          = {0};
    size_t      cch         = 0;
    TCHAR       *pszRet     = NULL;
    
    // 指定された属性の値を読み込む
    hr = pNodeMap->getNamedItem(pszNodeName, &pAttlibute);
    VariantInit(&vt);
    hr = pAttlibute->get_nodeValue(&vt);
    
    // 読み込んだ値をTCHARに変換
    // (CString使えばもっと簡単?)
    hr = StringCchLength(vt.bstrVal, STRSAFE_MAX_CCH, &cch);
    pszRet = (PTCHAR)malloc(sizeof(TCHAR) * (++cch));
    StringCchCopy(pszRet, cch, vt.bstrVal);
    
    RELEASE_OBJ(pAttlibute);
    VariantClear(&vt);
    
    return pszRet;
}
 
エラー処理を省いて大分見通しが良くなりましたが、それでも見慣れない型が多いですね。
 
とりあえず COMのの使用開始・終了に CoInitializeEx(), CoUninitialize()、 BSTRの生成・破棄に SysAllocString(), SysFreeString()、 VARIANTの初期化、クリアに VariantInit(), VariantClear()といったあたりを押さえておけばなんとかなるのでしょうか...。
 

0 件のコメント: