MSXMLのノード名取得について

解決


超初心者  2009-09-15 02:59:21  No: 70930

毎度お世話になります。

現在Win32 コンソールアプリでMSXMLを使ってのXML読み書き処理の作成を行っています。

テンプレートxmlを読み込んで、その中のルートエレメント名やノード名を取得しようと思っているのですが、xmlが

<root>
  <datas>
    <key>${0}</key>
    <value>${1}</key>
  </datas>
</root>

こういう形の場合はうまく取得できるのですが、

<?xml version="1.0" encoding="utf-8"?>
<root>
  <datas>
    <key>${0}</key>
    <value>${1}</value>
    <extdatas>
      <extra>${2-}</extra>
    </extdatas>    
  </datas>
</root>

こういう形になると、うまく取得が出来ずに困っています。具体的には、<extdatas>の中の<extra>ノード名の取得がうまくいきません。ソースを載せます。

// 読み込みの起点であるルートエレメントの取得
MSXML2::IXMLDOMElementPtr depRoot = pDoc->documentElement;

// ルートエレメント名取得
m_bsRootName = depRoot->nodeName;

MSXML2::IXMLDOMNodeListPtr dnpNodeList = depRoot->childNodes;

// ノードの数を取得
int nNodeListSize = dnpNodeList->Getlength();
if( nNodeListSize == 0 )
{
    _tprintf( _T("ノードが見つかりませんでした。") );
    return false;
}

// ノードの数だけ回す
for( int i=0; i<nNodeListSize; i++ )
{
    MSXML2::IXMLDOMElementPtr depNode = dnpNodeList->Getitem(i);
    // ノード名取得
    m_bsNodeName = depNode->nodeName;

    // 子ノード
    MSXML2::IXMLDOMNodeListPtr dnpChildNodeList = depNode->childNodes;
    // 子ノードの数を取得
    int nChildNodeListSize = dnpChildNodeList->Getlength();

    if( nChildNodeListSize != 0 )
    {
      // 子ノードの数分回す
      for( int j=0; j<nChildNodeListSize; j++ )
      {
        MSXML2::IXMLDOMElementPtr depChildNode = dnpChildNodeList->Getitem(j);

        // ノード名取得
        m_vbsChildNodeNameList.push_back(depChildNode->nodeName);
        // 子ノードのテキスト取得
        m_vbsNodeTextList.push_back(depChildNode->text);
      }
    }
}

このソースでは、最初のxmlの形では取得できるのですが・・・・・・・・

なにぶん素人なのでソースが間違えているかもしれませんので、その時はご指摘宜しくお願い致します。

そもそも、こういう事事態しないものなのでしょうか?


超初心者  2009-09-15 04:55:35  No: 70931

追加で質問です。

XMLLiteを使いたいので、Microsoft Windows SDK for Windows Vistaをインストールしました。それで、今作成しているプロジェクト内のヘッダで

#include <xmllite.h>
#pragma comment(lib, "xmllite.lib")

としましたが、

c:\users\korosuke.fst\documents\visual studio 2005\projects\sample\sample\stdafx.h(46) : fatal error C1083: include ファイルを開けません。'xmllite.h': No such file or directory

と出てしまいます。インストールした場所が悪いと思うのですが、この場合、どこに何をおけばいいのでしょうか?

宜しくお願い致します。


Blue  2009-09-15 18:18:20  No: 70932

下の質問のみ。

>#include <xmllite.h>
#include "xmllite.h" のほうが適切かも。
インクルード先のパスが解決できないのですかね?
Visual Studio2005であれば、
構成プロパティ→C/C++→全般→追加のインクルードディレクトリ
でパスを追加できます。


Blue  2009-09-15 18:22:51  No: 70933

最初の質問は単に xml の階層が問題なのでは?

<root>
    <datas>
        <key>${0}</key>
        <value>${1}</key>
    </datas>
</root>

<root>
    <datas>
        <key>${0}</key>
        <value>${1}</value>
        <extdatas>
            <extra>${2-}</extra>
        </extdatas>
    </datas>
</root>

インデントさせてみると、上は最大3階層、下は4階層で、ソースは3階層目までしか見ていない。
こういう階層がいくつあるか分からない場合は、再帰呼び出しを使ってたどります、


超初心者  2009-09-15 19:15:58  No: 70934

>Blueさん

ありがとうございます!おかげでパスを追加してインクルード出来ました^^
新しくライブラリを追加した時はパスを自分で追加する設定が必要なのをすっかり忘れていました^^;ありがとうございました!

>こういう階層がいくつあるか分からない場合は
そうなんです。いくつ階層があるかわからないXMLを読み込んだ時にどうすればいいのかわからなくて・・・・・・・・。

>再帰呼び出しを使ってたどります
すみません、再帰呼び出しとは具体的にどういったことをいうのでしょうか?もし宜しければ御教え願えませんが?また参考になるサイトなどを教えてもらえるとありがたいです。

自分でも再帰呼び出しについて調べてやってみようと思います。

宜しくお願い致します。


Blue  2009-09-15 19:23:47  No: 70935

ちょーてきとう。
テキストノードだけでもchildNodes->lengthが0でないくせに
childNodes->item[i]がNULLになるのはわからない。

#include <vector>
#import "msxml6.dll" rename_namespace("msxml")

void AddNodeName(std::vector<_bstr_t>& nodeNameList, msxml::IXMLDOMElementPtr& node)
{
    if (node == NULL) {
        return;
    }
    nodeNameList.push_back(node->nodeName);
    msxml::IXMLDOMNodeListPtr childNodes = node->childNodes;
    for (long i = 0L; i < childNodes->length; ++i)
    {
        msxml::IXMLDOMElementPtr childNode = childNodes->item[i];
        AddNodeName(nodeNameList, childNode);
    }
}

HRESULT test()
{
    HRESULT hr = S_OK;

    try
    {
        msxml::IXMLDOMDocumentPtr doc("MSXML.DOMDocument");
        doc->validateOnParse = VARIANT_FALSE;
        doc->load(L"foo.xml");

        msxml::IXMLDOMElementPtr root = doc->documentElement;
        std::vector<_bstr_t> nodeNameList;
        AddNodeName(nodeNameList, root);
    }
    catch (_com_error& e)
    {
        // エラー
        hr = e.Error();
    }
    return hr;
}

int main() 
{
    HRESULT hr = ::CoInitialize(NULL);
    if (FAILED(hr)) {
        return 1;
    }
    hr = test();
    ::CoUninitialize();
    return 0;
}


超初心者  2009-09-15 19:46:11  No: 70936

>Blueさん

早速のサンプルありがとうございます。
再帰とは、関数内で自分の関数を呼び出すことなのですね。また、スタックオーバーフローとかいうのも考慮しないといけないとのとこ。ここらへんはまた調べて勉強します。

このサンプルを参考に作成してみようと思います^^。

また質問があった時は宜しくお願い致します。


超初心者  2009-09-15 23:08:54  No: 70937

またまた質問させてください。今XMLLiteで作成中なのですが、自分が参考にしているサイトのサンプルで

int APIENTRY _tWinMain(HINSTANCE hInstance, 
    HINSTANCE hPrevInstance, LPTSTR lpCmdLine, int nCmdShow)
{
    UNREFERENCED_PARAMETER(hPrevInstance);
    UNREFERENCED_PARAMETER(lpCmdLine);

    Run();// 実際に処理している関数

    return 0;
}

というのがありました。_tWinMain関数とは一番最初に実行される関数だそうですが、mainでしたらプログラム中に既に

int _tmain(int argc, TCHAR* argv[], TCHAR* envp[])

が存在しています。XMLLiteを使う時は_tmainではなく_tWinMainを使わないといけないのでしょうか?

UNREFERENCED_PARAMETER(hPrevInstance);
UNREFERENCED_PARAMETER(lpCmdLine);

これが必須でなければ別に_tmainでもいいかと思うのですが・・・・・・・・・

宜しくお願い致します。


Blue  2009-09-15 23:11:38  No: 70938

UNREFERENCED_PARAMETERってなにかWeb検索してみては?

そしたら_tmainでもいいことがわかりますよ。


超初心者  2009-09-15 23:43:50  No: 70939

Blueさんお世話になります。

ちょっと調べてみました。

HINSTANCE hInstance・・・プログラム(インスタンス)を識別する番号
HINSTANCE hPrevInstance・・・アプリケーションの以前のインスタンスを識別(WIN32アプリケーションの場合は常にNULL)
LPTSTR lpCmdLine・・・コマンドラインから受け取る文字列
int nCmdShow・・・ウィンドウの状態?

UNREFERENCED_PARAMETER・・・変数が一度も使われていないという警告を回避するためだけのもの?

だとしたら、全然_tmainで構わないですね^^;HINSTANCE hPrevInstanceとかはちょっと存在意味が分からなかったですが・・・・。

調べて見てスッキリしました^^。

また質問ありましたら宜しくお願い致します。


超初心者  2009-09-17 01:31:23  No: 70940

すみません、質問させてください。

XMLLiteで、サイトを参考にxml書き出し処理を作成しています。

  if( FAILED(pWriter->WriteStartDocument(XmlStandalone_Omit)) )
  {
    _tprintf( _T("WriteStartDocument失敗") );
    return false;
  }
  if( FAILED(pWriter->WriteStartElement(NULL, L"sample", NULL)) )
  {
    _tprintf( _T("WriteStartElement失敗") );
    return false;
  }
  if( FAILED(pWriter->WriteStartElement(NULL, L"requirement", NULL)) )
  {
    _tprintf( _T("WriteStartElement失敗") );
    return false;
  }
  if( FAILED(pWriter->WriteElementString(NULL, L"type", NULL, L"ヘッダ")) )
  {
    _tprintf( _T("WriteElementString失敗") );
    return false;
  }
  if( FAILED(pWriter->WriteElementString(NULL, L"name", NULL, L"XmlLite.h")) )
  {
    _tprintf( _T("WriteElementString失敗") );
    return false;
  }
  if( FAILED(pWriter->WriteFullEndElement()) )
  {
    _tprintf( _T("WriteFullElement失敗") );
    return false;
  }
  if( FAILED(pWriter->WriteEndDocument()) )
  {
    _tprintf( _T("WriteEndDocument失敗") );
    return false;
  }
  if( FAILED(pWriter->Flush()) )
  {
    _tprintf( _T("Flush失敗") );
    return false;
  }

これで、

<?xml version="1.0" encoding="UTF-8"?>

<sample>
  <requirement>
    <type>ヘッダ</type>
    <name>XmlLite.h</name>
  </requirement>
</sample>

こういう形のXMLを書けるのですが、pWriter->WriteFullEndElementとpWriter->WriteEndDocumentはどっちか一方がなくてもきちんと上記の形で出力されます。

両方要素を閉じる動きだという認識でいるのですが、どういう違いがあるのでしょうか?

pWriter->WriteEndDocumentはルートエレメントを閉じるためのもの、pWriter->WriteFullEndElementはルートエレメント以下のノードを閉じるためのもの、ということなのでしょうか?

どなたかご教授いただけるとありがたいです。

宜しくお願い致します。


Blue  2009-09-17 01:34:44  No: 70941

質問の内容が変わったのであれば、ここは解決にして新しいスレッドを立てるほうがよいと思います。
ちなみに私はXMLLiteは使ったことないので解答できませんけど、、、

それと、ソースを載せるときはインデントさせましょう。
タブのままだとうまくいかないときがあるので、半角空白に置換するとよいです。


超初心者  2009-09-17 01:42:18  No: 70942

Blueさんおせわになります。

そうですね。無駄にスレを伸ばすだけですものね^^;

御指摘ありがとうございます。

当初の質問は解決しましたので、別にスレッドを立てさせて頂きます。

ありがとうございました。


※返信する前に利用規約をご確認ください。

※Google reCAPTCHA認証からCloudflare Turnstile認証へ変更しました。






  このエントリーをはてなブックマークに追加