XMLを使ってみる(その4)

任意のエレメントを取得する

XMLオブジェクトの子オブジェクトにアクセスする方法を試してみます。
例として、このblog自身のRSSを元にして、次のようなXMLオブジェクトを用意し、これを取り扱ってみます。

// XML without namespace
var aRDF:XML =
  <RDF>
  <channel about="http://d.hatena.ne.jp/octech/rss">
    <title>octech</title>
    <link>http://d.hatena.ne.jp/octech/</link>
    <description>octech</description>
  </channel>
  <item about="http://d.hatena.ne.jp/octech/20080620">
    <title>TortoiseSVNでUTF-8ファイルを比較する</title>
    <link>http://d.hatena.ne.jp/octech/20080620</link>
    <creator>octech</creator>
    <date>2008-06-20T16:30:43+09:00</date>
  </item>
  <item about="http://d.hatena.ne.jp/octech/20080619">
    <title>XMLを使ってみる(その3)</title>
    <link>http://d.hatena.ne.jp/octech/20080619</link>
    <creator>octech</creator>
    <date>2008-06-19T19:48:31+09:00</date>
  </item>
  </RDF>;

上記のXMLオブジェクトである aRDF には、3つのXMLオブジェクト(channne, item, item)が子ノードとして存在しています。この子ノードのうち、itemオブジェクトのみを抜き出すには aRDF.item 、たったこれだけの表現でいいのです!
このオブジェクトは XMLList というクラスのオブジェクトなのですが、次のコードでその取得を検証してみます。

trace( aRDF.item.length() );   // 2
trace( typeof(aRDF.item) );    // xml
trace( aRDF.item is XML );     // false
trace( aRDF.item is XMLList ); // true

for each( var xmlItem:XML in aRDF.item ){
  trace( xmlItem.title );
}

// 出力結果.
// [trace] 2
// [trace] xml
// [trace] false
// [trace] true
// [trace] TortoiseSVNでUTF-8ファイルを比較する
// [trace] XMLを使ってみる(その3)

itemノードのタイトルを簡単にリスト表示できました。

XMLのNamespace

簡単にXMLを扱えるE4Xですが、ネームスペースが入ってきた途端に扱いがちょっと面倒になります。
例えば、上記のXMLのサンプルの最初の部分を以下のように変更し、ネームスペースを定義してみます。

// XML with namespace
var aRDF:XML =
  <rdf:RDF
    xmlns="http://purl.org/rss/1.0/"
    xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
    xmlns:content="http://purl.org/rss/1.0/modules/content/"
    xmlns:dc="http://purl.org/dc/elements/1.1/"
  >
  // --- この間は全く同じXML --- //
  </rdf:RDF>

xmlns というのは XMLのNameSpace という意味で、大概のRDFは上記のようにネームスペースを3つ4つ定義しており、例えば上記のXML表現は、次のような意味になります。

上記のネームスペースを入れたXMLで、次のようなコードを書いて、ルートノードの名前を表示してみます。

trace( aRDF.name() );

// 出力結果.
// [trace] http://www.w3.org/1999/02/22-rdf-syntax-ns#::RDF

つまり、XMLにおけるネームスペースとはこのようにエレメント名に影響を及ぼす、ということらしいので、ネームスペースを定義し という表現を書いたら、それはもともとの とは全く違うモノとして扱う必要があるのです。
この rdf:: という表現をネームスペースのプレフィックスと言います。

デフォルトネームスペースの悪夢

問題は、デフォルトネームスペースの扱いなのです。
上記のネームスペースを入れたXMLでitemエレメントを取得してみましょう。先ほどまで itemノード の数が 2 と出力されていた次のコードで、0となってしまします。
これは、素のitemノードを探したけど見つからなかった、ということが原因なのです。そのことを確認するために次のコードを実行してみると、子ノードのエレメント名一覧を取得することが出来ます。

// XML with namespace版で試す.
trace( aRDF.item.length() );  // 0

for each( var xmlChild:XML in aRDF.children() ){
  trace( xmlChild.name() );
}

// 出力結果.
// [trace] 0
// [trace] http://purl.org/rss/1.0/::channel
// [trace] http://purl.org/rss/1.0/::item
// [trace] http://purl.org/rss/1.0/::item

何もプレフィックスをつけていないエレメント名にデフォルトネームスペースが適用されているのが分かります。
ここで登場するのがE4X準拠のクラスNamespaceです。
ネームスペースのプレフィックスをコンストラクタに渡すとそのNamespaceオブジェクトが作成されるので、そのオブジェクトをXML表現に入れることで実行時にネームスペースをうまく処理できるようになるのです。
つまり、以下のコードのように処理を行えば、itemエレメントの子エレメントであるtitleの文字列要素を取得できるのです。

// XML with namespace版で試す.
var ns_default:Namespace  = aRDF.namespace("");
trace( aRDF.ns_default::item.length() );  // 2

for each( var xmlItem:XML in aRDF.ns_default::item ){
  trace( xmlItem.ns_default::title );
}

// 出力結果.
// [trace] 2
// [trace] TortoiseSVNでUTF-8ファイルを比較する
// [trace] XMLを使ってみる(その3)

デフォルトネームスペースはE4X側でも何も指定しなくても処理できたらいいのになー、と思うのですが、それはそれでいろいろ問題が発生する可能性があるのかもしれません。そんな自分はXMLの仕様とかちょっと勉強した方がいいかもしれません。

10日でおぼえるXML入門教室 第2版

10日でおぼえるXML入門教室 第2版