Nokogiri で名前空間のprefixの付いた要素を検索する2010/04/20

概要

タイトルのとおり(ぇ

某所のOpenSerach APIを叩いてatomで結果を得たと仮定。 レスポンス中の、

<feed xmlns="http://www.w3.org/2005/Atom" xmlns:opensearch="http://a9.com/-/spec/opensearch/1.1/">
  (略)
  <opensearch:totalResults>21</opensearch:totalResults>
  (略)
</feed>

この要素を取り出したいとする。

doc = Nokogiri::XML(atom)
doc/'openserch:totalResults'
#=> []

むむう。

原因は以下の2点

  • /(Nokogiri::XML::Node#searchのエイリアス)に名前空間の情報が与えられていない
  • 引数の'openserch:totalResults'がCSSセレクタの記述と曖昧(少なくともNokogiriにとって)

名前空間を指定する。

Nokogiriは(バックエンドであるlibxmlの機能により)名前空間を結構真面目に扱う。

Nokogiri::XML::Node#at, #search, #xpath では、引数の最後に名前空間情報のHashを取る。

see also http://nokogiri.rubyforge.org/nokogiri/Nokogiri/XML/Node.html#M000241

doc.at('.//opensearch:totalResults', {'opensearch' => 'http://a9.com/-/spec/opensearch/1.1/'}).content
#=> "21"

キーが名前空間のprefix、値がスキーマのURI、これはxml中で指定されるものと一致しなくてはならない。 逆にprefixの方は引数の間で整合性が取れていれば、xml中の記述と一致する必要は無い。 (そんなことをする必要があるとも思えないが……)

doc.at('.//os:totalResults', {'os' => 'http://a9.com/-/spec/opensearch/1.1/'}).content
#=> "21"

引数の最初の'.//'はxpathであることを明示するための指定

厳密には、Nokogiri::XML::Node#at, #search では、引数が'./'または'/'で始まる場合、引数はxpathだと見なす。 './/'なのは子ノードも検索対象にするため。

めどい

とりあえず、最初の目的は達成されたのだが……。

'.//' だとどこか他に opensearch:totalResults と同名の要素が存在した場合に曖昧になるので、pathをきっちり書きたいと思うと、 もうひとつ面倒に直面することなる。

doc.at('./feed/opensearch:totalResults', {'opensearch' => 'http://a9.com/-/spec/opensearch/1.1/'})
#=> nil

見付からない……。

実は名前空間を指定するHashを省略した場合は、内部で document.root.namespace が補完されているのだが、引数で名前空間を明示された場合は引数のHashにマージしてくれたりはしないのだ。 そのため、rootの名前空間も明示的に指定する必要がある。

doc.at('./atom:feed/opensearch:totalResults', {'atom' => 'http://www.w3.org/2005/Atom', 'opensearch' => 'http://a9.com/-/spec/opensearch/1.1/'}).content
#=> "21"

これが正解。

後でパッチでも書こう。

コメント

コメントをどうぞ

※メールアドレスとURLの入力は必須ではありません。 入力されたメールアドレスは記事に反映されず、ブログの管理者のみが参照できます。

※なお、送られたコメントはブログの管理者が確認するまで公開されません。

名前:
メールアドレス:
URL:
コメント:

トラックバック

このエントリのトラックバックURL: http://dragonstar.asablo.jp/blog/2010/04/20/5031744/tb

※なお、送られたトラックバックはブログの管理者が確認するまで公開されません。