2011-01-14 5 views
32

Sto cercando di estrarre l'elemento dc:title utilizzando un percorso x. Posso estrarre i metadati usando il seguente codice.Richiesta spazio nomi Nokogiri/Xpath

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> 

Come si può vedere, sembra funzionare correttamente. Tuttavia, non sembra che sia in grado di ottenere le informazioni sul titolo da questo albero dei nodi, tutti i seguenti errori non riescono.

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 

Qualcuno potrebbe spiegare come utilizzare gli spazi dei nomi in un percorso x con il documento xml sopra riportato.

risposta

60

Tutti i namespace devono essere registrati durante l'analisi. Nokogiri registra automaticamente gli spazi dei nomi sul nodo radice. Qualsiasi spazio dei nomi che non si trova sul nodo radice devi registrarti. Questo dovrebbe funzionare:

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

In alternativa, è possibile rimuovere completamente gli spazi dei nomi. Fallo solo se sei sicuro che non ci saranno nomi di nodi in conflitto.

doc.remove_namespaces! 
puts doc.xpath('//title') 
+0

Awesome ha funzionato perfettamente, grazie! – Jamie

+3

+1 Sì remove_namespaces FTW! –

+1

Grazie !!! questa è magia! – Jirapong

1

Con correttamente prefisso opf registrato per 'http://www.idpf.org/2007/opf' namespace URI, e dc per 'URI', è necessario:

/*/opf:metadata/dc:title 

Nota: xmlns e xml sono riservati prefissi che non può essere associato a qualsiasi altro spazio dei nomi URI rispetto al built-in 'http://www.w3.org/2000/xmlns/' e 'http://www.w3.org/XML/1998/namespace'.

+0

Non sembra funzionare doc.xpath ('/ */opf: metadata/dc: title') # => "' valuta ': prefisso spazio dei nomi non definito " – Jamie

+0

@Jamie: hai effettivamente letto la risposta? Inizia la prima frase * "Con prefisso correttamente registrato" * ... –

+0

Scuse di Alejandro Non capisco completamente c'è un modo per farlo senza il prefisso per opf (eccetto il modo descritto nella risposta @ mark-thomas), Sarebbe bello farlo in una query xpath. – Jamie

0

In alternativa alla costruzione in modo esplicito un hash di URI dello spazio dei nomi, è possibile recuperare le definizioni di namespace dall'elemento XML dove sono definiti.

Usando il tuo esempio:

# 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) 

Nota che il secondo XPath potrebbe essere stato anche:

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

Ma perché ricerca dalla radice quando si ha un antenato più vicino? Inoltre, dovresti considerare l'elemento che definisce il namespace più i suoi figli come "scope" dello spazio dei nomi. La ricerca in un ambito limitato è meno confusa ed evita i bug sottili.