xerces-c++ に取り組んでみた その1
ひでのふは主にWindows上で動くプログラムを作る仕事をしているわけですが
たまーに unix とか linux で動かすプログラムを作る必要もあったりするわけです。

で、Windows上で動くプログラムの設定関連はレジストリを使っているわけですが
linux にはそんなものはありません。
そうなると、Windows用に作ったプログラムをlinuxに移植する際に、
レジストリ周りの処理を、設定を書いたファイルへのアクセス処理に変更する必要が
でてきます。

次に設定ファイルをどのようなフォーマットで書くか。
ini?csv?バカ言っちゃいけない。もうこれは XML 以外の選択肢はありません

XML をプログラムから扱うとなると、まずはXML翻訳機能を司る「パーサ」を選びます。
いろいろ試行錯誤した結果から言ってしまうと、
パーサ勉強するなら自分でパーサ作った方が早い
んですが、再発明は往々にして無駄な努力以外の何者でもないので
既に世の中に出回っているパーサを選択します。

linux への移植性の向上が目的の一つなので、MSXML は除外します。
残りは expat と xerces と XML4C ぐらいしかないわけですが
xerces が一番がメジャーと言う大変非科学的な理由で xerces にします。
てゆーか、Apache のやってる xerces と IBM のやってる XML4C は大変距離が近いため
ひでのふは xerces のプログラミングを XML4C のドキュメントを参考にすると言う
初歩的かつ根源的なミスに気が付かないまま、
うごかねーうごかねーと言っていたのは内緒です。

ちなみに XML4C は java と C++ 間の移植が簡単に出来ることを念頭において
設計されていますが、xerces は「java っぽくならない」ことを念頭に
設計されています。
java のアーキテクチュアがお気に召さないひでのふは、xerces を選択するのも
むべなるかなと言ったところでしょうか。

次に XML へのアクセス方式で、DOM と SAX のどちらを選択するかですが
レジストリの置き換えが目的なので、迷わず DOM です。
ちなみにDOMとSAXの違いは...
・DOM は XML を一気に読んで翻訳(parse)する。
・SAX は XML を1行読むたびに派生クラスのメソッドを呼び出す。
どちらも一長一短ありますが、DOM の方がレジストリの考え方に近いと思います。

最後に、参考書籍っつーかwebページですが、java で XML ですと結構 web ページも
充実しているんですが、xerces C++ に関しては日本語で書かれているものは
全く見当たりませんでした。
仕方がないので、java の記述を脳内でC++に変換したり、
Distribution にくっついてくる英語のリファレンスを翻訳してみたりと、
大変苦労しました。

すでに誰かが書いていることを、わざわざ書かないをモットーとしておりますが
どうも世の中には見当たらないようなので、こうして公開することにしました。

使用している xerces はここから Binary Distribution をダウンロードしました。
バージョンは Xerces-C++ 2.8.0 です。
開発環境は Visual Studio 2005 を使用していますが
環境依存にならないようなソースを書くよう心がけています。
続きを読む
【2008/02/07】 Xerces-C++ | トラックバック(0) | コメント(3) | page top↑
xerces-c++ に取り組んでみた その2
まず、XML の解説っつーとこんな分厚い本を買わされる嵌めになるわけですが
ただタグで括っただけの代物の扱いになんでこんな情報量が必要なの?
と思うのは当然かと思われます。ひでのふもそう思っていました。

XML 技術においては、XML 文書をデータ、XSL ファイルをスクリプトと
することでデータ変換プログラムの一丁あがりと言う使い方があります。
となると、プログラミング言語的な使い方もあるという事になり、
必然的にその情報量も多くならざるを得ないと言うのは想像できるかと思います。
また、XML から派生した技術も多く(SOAPとかな)それについても解説を加えるならば
これでもまだまだ足りないと言うことになります。

しかし、xerces の DOM で XML を扱うだけなら、それほど必要な知識はありません。

XML 文書とはタグで括られたテキストであると言う理解以外に必要な知識を
下記にまとめておきます。

・整形式のXML文書
→ XML の書式に従って書かれたテキストファイルを指します。
例え XML っぽい文書であっても(終了タグが欠けているとか)
整形式のXML文書で無い場合は、xerces は一切解析をしてくれません。
テキストエディタで XML 文書を作ると、知らず知らずのうちに整形式のXML文書で
なくなっていることがあるので、注意が必要です。

XML 1.0 の書式は JIS 規格で定義されています。
こっちの方がちょっとだけ読みやすいけれど、少し古いので注意。

・Document
→ XML 文書ファイルを指します。

・Element
→ タグで区切られたデータのかたまりを指します。例えば

<element attr="image.html">
 <node>イメージデータ</node>
</element>

と言った感じです。

・Node
→ 各要素を指します。例えば

<node>ノードデータ</node>

上記の例は タグを表す Node(Element の Node)と、
テキストデータを表す Node(Text の Node )の二つに分けられることになります。

ひでのふ的には Node と Element をごっちゃにしないことは、
XML への理解を進める上で、大変重要
だと考えます。

・Attribute
→ タグの中に書かれた属性を示します。例えば

<tags attr="image.html">イメージデータ</tags>

斜字の部分が Attribute です。
【2008/02/14】 Xerces-C++ | トラックバック(0) | コメント(12) | page top↑
xerces-c++ に取り組んでみた その3
ではまず DOM が使えることを確認しましょう。
以下のコードがビルドできることを確認してください。

-------- ここから --------

#include <stdio.h>
#include <xercesc/parsers/XercesDOMParser.hpp>
#include <xercesc/util/PlatformUtils.hpp>
#include <xercesc/dom/DOM.hpp>
#include <xercesc/sax/SAXException.hpp>

#ifdef XERCES_CPP_NAMESPACE_USE
XERCES_CPP_NAMESPACE_USE
#endif

void main(int argc, char* argv[]) ;

void main(int argc, char* argv[])
{
  DOMDocument *p_document ; // C++
  XercesDOMParser *p_dom_parser ;

  try
  {
// initialize the XML library
    XMLPlatformUtils::Initialize() ;
    p_dom_parser = new XercesDOMParser() ;
    p_dom_parser -> parse( "xerces-test.xml" ) ;
    p_document = p_dom_parser -> getDocument() ;
    if( p_document != NULL )
    {
      printf( "XML File Opened\r\n" ) ;
    }
    else
    {
      printf( "XML File Open Failure\r\n" ) ;
    }
// terminate the XML library
    delete p_dom_parser ;
    XMLPlatformUtils::Terminate() ;
  }
  catch( DOMException dom_e )
  {
    printf( "Encount DOMException / code = %d / message = %s\r\n",
      dom_e.code,
      XMLString::transcode( dom_e.getMessage()) ) ;
  }
  catch( SAXException sax_e )
  {
    printf( "Encount SAXException / message = %s\r\n",
      XMLString::transcode( sax_e.getMessage()) ) ;
  }
  catch(...)
  {
    printf( "Encount Exception\r\n" ) ;
  }
}

-------- ここまで --------

・コンパイルが通らないときは、ヘッダが見えてないか、コピペしたソースの
全角スペースを削除し切れてないかのどちらかです。
・リンカが通らないときは、XMLのライブラリが見えてないからです。

とりあえず、動く動かないは別にして、ビルドが通れば準備はOKです。

出来た実行ファイルから見えるところに、以下の内容で xerces-test.xml を作成すると

-------- ここから --------
<?xml version="1.0" encoding="UTF-8"?>
<dataset>
データセットっす。
</dataset>
-------- ここまで --------

コンソールに XML File Opened と表示されるはずです。

【ソースコードの解説】

・XMLPlatformUtils::Initialize() ;
・XMLPlatformUtils::Terminate() ;
この二つのメソッドを、それぞれプログラムの先頭と終わりで呼び出す必要があります。

・XercesDOMParser クラス
xerces に DOM で XML を解析させるのはとても簡単で、単に XercesDOMParser クラスの
インスタンスを起こし、parse メソッドの引数に XML ファイル名を与えるだけです。

うんまあ解析結果を取り出す方法に辿り着くのが激しくめんどくさかったわけですが。

・メソッド失敗時の戻り値は NULL
parse メソッドに引き渡したファイルが存在しないぐらいでは例外は吐きません。
上記のソースのように、戻り値のポインタを判断して処理を進める必要があります。
これは DOM と頭に付くクラスのメソッドに、すべて共通した性質です。

・try ~ catch
それでも一応リファレンスには例外吐くよって書いてあるので、
try ~ catch は必須です。
捕まえるべき例外は DOMException と SAXException です。
XMLException ってのもあるんですが、これは XMLException から派生した例外を
自分で作って吐かない限り、捕まえる必要はありません。

・文字列
xerces の文字列はすべて XMLCh 型です。中身は UTF-16 文字列です。
char ポインタはそのままでは扱えません。
printf みたいに文字列を char ポインタで引き渡す必要がある場合は、

XMLString::transcode( e.getMessage()) ) ;

のようにする必要があります。

【2008/02/15】 Xerces-C++ | トラックバック(0) | コメント(0) | page top↑
xerces-c++ に取り組んでみた その4
次に XML 文書の中身が取得できるようにします。
以下のコードがビルドできることを確認してください。
使用する XML 文書は前回と同じです。

-------- ここから --------

#include <stdio.h>
#include <xercesc/parsers/XercesDOMParser.hpp>
#include <xercesc/util/PlatformUtils.hpp>
#include <xercesc/dom/DOM.hpp>
#include <xercesc/sax/SAXException.hpp>

#ifdef XERCES_CPP_NAMESPACE_USE
XERCES_CPP_NAMESPACE_USE
#endif

void main(int argc, char* argv[]) ;
void view_xml( DOMDocument *pDoc ) ;

void main(int argc, char* argv[])
{
  XercesDOMParser *p_dom_parser ;
  DOMDocument *p_document ;

  try
  {
// initialize the XML library
    XMLPlatformUtils::Initialize();
    p_dom_parser = new XercesDOMParser();
    p_dom_parser -> parse( "xerces-test.xml" ) ;
    p_document = p_dom_parser -> getDocument() ;
    if( p_document != NULL )
    {
      printf( "XML File Opened\r\n" ) ;
      view_xml( p_document ) ;
    }
    else
    {
      printf( "XML File Open Failure\r\n" ) ;
    }
// terminate the XML library
    delete p_dom_parser ;
    XMLPlatformUtils::Terminate() ;
  }
  catch( DOMException dom_e )
  {
    printf( "Encount DOMException / code = %d / message = %s\r\n",
      dom_e.code,
      XMLString::transcode( dom_e.getMessage()) ) ;
  }
  catch( SAXException sax_e )
  {
    printf( "Encount SAXException / message = %s\r\n",
      XMLString::transcode( sax_e.getMessage()) ) ;
  }
  catch(...)
  {
    printf( "Encount Exception\r\n" ) ;
  }
}

void view_xml( DOMDocument *pDoc )
{
  DOMElement *p_element ;
  DOMNode *p_node ;

  p_element = pDoc -> getDocumentElement() ;
  if( p_element != NULL )
  {
    printf( "Element : %s\r\n",
      XMLString::transcode( p_element -> getNodeName())) ;
    printf( "Element Value : %s\r\n",
      XMLString::transcode( p_element -> getNodeValue())) ;
    p_node = p_element -> getFirstChild() ;
    if( p_node != NULL )
    {
      printf( "Node Type : %d\r\n", p_node -> getNodeType()) ;
      printf( "Node Name : %s\r\n",
        XMLString::transcode( p_node -> getNodeName())) ;
      printf( "Node Value : %s\r\n",
        XMLString::transcode( p_node -> getNodeValue())) ;
    }
  }
}

-------- ここまで --------

実行ファイルを実行すると、コンソールに以下の内容が表示されるはずです。

XML File Opened
Element : dataset
Element Value : (null)
Node Type : 3
Node Name : #text
Node Value :
データセットっす。

今度はノードの中身を表示することができました。

【ソースコードの解説】

・view_xml 関数
前回との main 関数との違いは、この view_xml 関数を呼び出しているかいないか
だけです。
xerces で DOM を使用する場合、初めと終わりは常に同じ処理を行うので、
次回からは main 関数は省略し、view_xml 関数の内容だけを書くことにします。

・DOMDocument::getDocumentElement メソッド
Document からルートの Element を得るためのメソッドです。
ルートの Element は XML 文書一つに付きかならず一つと決まってるっぽいです。

・DOMElement::getNodeName メソッドと DOMElement::getNodeValue メソッド
Element の Node の名前を取得します。
これに違和感を感じるようでしたら、とりあえずそーゆーものと割り切ってください。
ちなみに、Element の名前は取れても DOMElement::getNodeValue メソッドは
必ず NULL を返します。

・DOMENode::getNodeName メソッドと DOMNode::getNodeValue メソッド
このソースでは、Text の Node の名前を取ろうとしているので、xerces が勝手に
振った名前が取られます。もちろん値は取得できます。
ちゃんと改行も込みで取得できているところもポイントです。

・DOMENode::getNodeType メソッド
戻り値は Node の種別を意味する short が返されます。
判定の必要があるなら、以下の enum 値が使えます。

DOMENode::ELEMENT_NODE = 1
DOMENode::ATTRIBUTE_NODE = 2
DOMENode::TEXT_NODE = 3
DOMENode::CDATA_SECTION_NODE = 4
DOMENode::ENTITY_REFERENCE_NODE = 5
DOMENode::ENTITY_NODE = 6
DOMENode::PROCESSING_INSTRUCTION_NODE = 7
DOMENode::COMMENT_NODE = 8
DOMENode::DOCUMENT_NODE = 9
DOMENode::DOCUMENT_TYPE_NODE = 10
DOMENode::DOCUMENT_FRAGMENT_NODE = 11
DOMENode::NOTATION_NODE = 12

getNodeType でぐぐると戻り値の日本語の説明が引っかかります。
java でも同じようですので、参考にすることができるでしょう。
また、TEXT_NODE だけを XML 文書から抽出したい!とかって思うのなら
DOMNodeFilter クラスの使用を検討してみると幸せになれるかもしれません。

何れにせよ、まだこの程度の XML 文書の読み込み程度では、レジストリの代わりに
するのは遠いです。
【2008/02/19】 Xerces-C++ | トラックバック(0) | コメント(0) | page top↑
xerces-c++ に取り組んでみた その5
次にお手近の Visual Studio .net のプロジェクトファイル(拡張子が .vcproj)を
開いてみてください。中身は XML です。

まさに、そのプロジェクトに関する設定が XML で保存されているわけですが
どのような書式で設定が保存されているかと言うと、タグとタグの間には
設定は一つもかかれておらず、以下のようにタグの中に設定が書かれています。

<VisualStudioProject
ProjectType="Visual C++"
Version="8.00"
Name="xerces-test"
ProjectGUID="{3035AD25-066D-4A25-BE6E-8D6E258D8E7B}"
RootNamespace="xercestest"
Keyword="Win32Proj"
>
    :
    :
</VisualStudioProject>

この、タグの中の設定を Attribute と呼びます。
レジストリの置き換えを目指すなら、Microshaft 的に Attribute に設定を置くのが
よかろうとも思えるので、Attribute を表示するソースを以下に示します。


-------- ここから --------

void view_xml( DOMDocument *pDoc )
{
  DOMElement *p_element ;
  DOMNode *p_node, *p_attr_node ;
  XMLCh attr[100];
  DOMNamedNodeMap *p_map ;

  p_element = pDoc -> getDocumentElement() ;
  if( p_element != NULL )
  {
    printf( "Element : %s\r\n",
      XMLString::transcode( p_element -> getNodeName())) ;
    printf( "Element Value : %s\r\n",
      XMLString::transcode( p_element -> getNodeValue())) ;
      XMLString::transcode( "href", attr, 99);
    printf( "Element Attribute : %s\r\n",
      XMLString::transcode( p_element -> getAttribute( attr ))) ;
    p_node = p_element -> getFirstChild() ;
    if( p_node != NULL )
    {
      printf( "Node Type : %d\r\n", p_node -> getNodeType()) ;
      printf( "Node Name : %s\r\n",
        XMLString::transcode( p_node -> getNodeName())) ;
      printf( "Node Value : %s\r\n",
        XMLString::transcode( p_node -> getNodeValue())) ;
    }
  }
}

-------- ここまで --------

次に、使用する XML ファイルの内容を以下に示します。

-------- ここから --------
<?xml version="1.0" encoding="UTF-8"?>
<dataset href="http://hidenov.co.ru/index.html">
データセットっす。
</dataset>
-------- ここまで --------

実行ファイルを実行すると、コンソールに以下の内容が表示されるはずです。

XML File Opened
Element : dataset
Element Value : (null)
Element Attribute : http://hidenov.co.ru/index.html
Node Type : 3
Node Name : #text
Node Value :
データセットっす。

Attribute "href" の値を表示することができました。

でも、ルートの Element にすべての設定値を書き込むのは、いくらなんでも乱暴です。


【2008/02/20】 Xerces-C++ | トラックバック(0) | コメント(0) | page top↑
| ホーム | OLD