読者です 読者をやめる 読者になる 読者になる

rubyを1.8.6に上げるとXPathが正しく動作しなくなる?

Ruby REXML

これと同じ問題で悩んでいます。バグじゃないかなー。

attribute::* で全ての属性が取得できるのに、attribute::class みたいに属性の名前を指定すると何も返ってこなくなるみたいでした。


(追記)

にバグレポートとパッチが上がっていました。

でも、XML名前空間の仕様を勉強しなおしていたら、本当にバグなのかわからなくなってきました。デフォルトの名前空間は属性に適用されないので、

A default namespace declaration applies to all unprefixed element names within its scope. Default namespace declarations do not apply directly to attribute names; the interpretation of unprefixed attributes is determined by the element on which they appear.

http://www.w3.org/TR/REC-xml-names/#defaulting

チュートリアルの Namespaces にあるとおり、

こういう場合は名前空間を明示的に指定しないと駄目なのかも。バグレポートの例だと、

#! /usr/bin/env ruby                                                                                

require 'rexml/document'
doc = REXML::Document.new '<doc xmlns="ns"><item name="foo"/></doc>'

# 下の2行は名前空間なしの属性にnsという名前空間を指定しているからうまくいかない。
p doc.root.elements["item"].attribute("name", "ns")
p doc.root.elements["item[@name='foo']"]

# 属性を明示的に名前空間なしに指定すればOK。
# REXMLの機能で、elements["item"]はデフォルトの名前空間が指定されているものとみなされる。
p doc.root.elements["item"].attribute("name", "")

# あるいは名前空間のURIを取得して
ns = doc.root.namespace
p ns

# 要素に名前空間を指定する。属性には名前空間なしを指定する。これでうまくいく。
p REXML::XPath.first(doc, "//m:item/@name", {"m" => ns})
p REXML::XPath.first(doc, "//m:item", {"m" => ns}).attribute("name", "")
p REXML::XPath.first(doc, "//m:item[@name='foo']", {"m" => ns})

実行結果は、

nil
nil
name='foo'
"ns"
name='foo'
name='foo'
<item name='foo'/>

というわけで、これで正しいような気もします。