web-dev-qa-db-ja.com

Nokogiri / Xpath名前空間クエリ

Xpathを使用してdc:title要素を取得しようとしています。次のコードを使用してメタデータを取得できます。

doc = <<END
<?xml version="1.0" encoding="UTF-8"?>
<package xmlns="http://www.idpf.org/2007/opf" version="2.0">
  <metadata xmlns:dc="URI">
    <dc:title>title text</dc:title>
  </metadata>
</package>
END

doc = Nokogiri::XML(doc)

# Awesome this works!
puts '//xmlns:metadata'
puts doc.xpath('//xmlns:metadata')
# => <metadata xmlns:dc="URI"><dc:title>title text</dc:title></metadata>

ご覧のとおり、上記は正しく動作しているようです。ただし、このノードツリーからタイトル情報を取得できないようです。以下のすべてが失敗します。

puts doc.xpath('//xmlns:metadata/title')
# => nil

puts doc.xpath('//xmlns:metadata/dc:title')
# => ERROR: `evaluate': Undefined namespace prefix

puts doc.xpath('//xmlns:dc:title')
# => ERROR: 'evaluate': Invalid expression: //xmlns:dc:title

上記のxml docを使用して、xpathで名前空間を使用する方法を誰かに説明してもらえますか?.

38
Jamie

すべての名前空間は、解析時に登録する必要があります。のこぎりは、ルートノードに名前空間を自動的に登録します。ルートノード上にない名前空間は、自分で登録する必要があります。これはうまくいくはずです:

puts doc.xpath('//dc:title', 'dc' => "URI")

または、名前空間を完全に削除することもできます。これは、競合するノード名がないことが確実な場合にのみ行ってください。

doc.remove_namespaces!
puts doc.xpath('//title')
71
Mark Thomas

'http://www.idpf.org/2007/opf'名前空間URIに適切に登録された接頭辞opf、および'URI'dcを使用すると、次のものが必要です。

/*/opf:metadata/dc:title

xmlnsおよびxmlは、組み込みの'http://www.w3.org/2000/xmlns/'および'http://www.w3.org/XML/1998/namespace'以外の名前空間URIにバインドできない予約済みのプレフィックスです。

1
user357812

名前空間URIのハッシュを明示的に作成する代わりに、名前空間定義が定義されているxml要素から名前空間定義を取得できます。

あなたの例を使用して:

# First grab the metadata node, because that's where "dc" is defined.
metadata = doc.at_xpath('//xmlns:metadata')

# Pass metadata's namespaces as the resolver.
metadata.at_xpath('dc:title', metadata.namespaces)

2番目のxpathも可能性があることに注意してください。

doc.at_xpath('//dc:title', metadata.namespaces).to_s

しかし、祖先が近い場合、なぜルートから検索するのでしょうか。また、名前空間を定義する要素とその子を名前空間の「スコープ」と見なす必要があります。限定されたスコープを検索することで混乱が少なくなり、微妙なバグを回避できます。

0
Kelvin