Nokogiri / Xpath query dello spazio dei nomi

Sto cercando di estrarre la dc:title element usando un xpath. Posso estrarre i metadati usando il seguente codice.

 doc = <<END    title text   END doc = Nokogiri::XML(doc) # Awesome this works! puts '//xmlns:metadata' puts doc.xpath('//xmlns:metadata') # => title text 

Come puoi vedere, sembra funzionare correttamente. Tuttavia, non sembra che sia in grado di ottenere le informazioni sul titolo da questo albero dei nodes, 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 xpath con il documento xml sopra.

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, è ansible rimuovere completamente gli spazi dei nomi. Fallo solo se sei sicuro che non ci saranno nomi di nodes in conflitto.

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

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

 /*/opf:metadata/dc:title 

Nota : xmlns e xml sono prefissi riservati che non possono essere associati a nessun altro URI dello spazio dei nomi di 'http://www.w3.org/2000/xmlns/' e 'http://www.w3.org/XML/1998/namespace' .

In alternativa alla costruzione esplicita di un hash di URI dello spazio dei nomi, è ansible recuperare le definizioni dello spazio dei nomi dall’elemento xml in cui 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) 

Si noti che il secondo xpath potrebbe essere stato anche:

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

Ma perché cercare dalla radice quando hai 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.