How to use xmlns declarations with XPath in Nokogiri
That XPath query looks for elements that are not in any namespace. You need to tell your XPath processor that you are looking for elements in the http://sdb.amazonaws.com/doc/2007-11-07/
namespace.
One way to do that with Nokogiri is:
doc = Nokogiri::XML.parse(...)
doc.xpath("//aws:Item/aws:Attribute[Name='Foo']/aws:Value", {"aws" => "http://sdb.amazonaws.com/doc/2007-11-07/"})
Using nokogiri xpath to access nested elements within an xmlns
It’s a namespacing issue:
datasource.xpath(
'subsystem:connection-url',
'subsystem' => 'urn:jboss:domain:datasources:1.2')
#⇒ [#<... name="connection-url" namespace=...
Nokogiri/Xpath namespace query
All namespaces need to be registered when parsing. Nokogiri automatically registers namespaces on the root node. Any namespaces that are not on the root node you have to register yourself. This should work:
puts doc.xpath('//dc:title', 'dc' => "URI")
Alternately, you can remove namespaces altogether. Only do this if you are certain there will be no conflicting node names.
doc.remove_namespaces!
puts doc.xpath('//title')
Ruby/Nokogiri xmlns and other extensions via xpath
add:cars
is an attribute of the price
element, not an element itself. The syntax you want is:
root.xpath("//xmlns:price/@add:cars")
or possibly even just
root.xpath("//@add:cars")
if you want the add:cars
attributes of all elements.
Note that since the namespaces are declared on the root, Nokogiri registers them automatically so you don’t need to include the mappings in your call to xpath
(you will need to include them if your document is more complex with namespaces being declared on non-root elements). Also the default namespace is registered with the prefix xmlns
, so you can use that in your XPath.
Nokogiri parse XML with xpath
With method 2, try using:
d.xpath('//feed/entry[title[node()]]'
This will return a nodeset containing nodes that have a non-empty title. Then you can iterate over set however you like.
Xpath - How to navigate to a value (Ruby Nokogiri)
require 'nokogiri'
doc = Nokogiri::XML(File.open("xml4.xml"))
target_date = "2015-02-09"
target_currency = 'USD'
xpaths = [
"//gesmes:Envelope",
"/xmlns:Cube",
"/xmlns:Cube[@time='#{target_date}']",
"/xmlns:Cube[@currency='#{target_currency}']",
]
xpath = xpaths.join
target_cube = doc.at_xpath(xpath)
puts target_cube.attribute('rate')
--output:--
1.1297
Response to comment:
Your root tag:
<gesmes:Envelope xmlns:gesmes="http://www.gesmes.org/xml/2002-08-01"
xmlns="http://www.ecb.int/vocabulary/2002-08-01/eurofxref">
...declares two namespaces with xmlns
, which stands for xml namespace. The namespace:
xmlns:gesmes="http://www.gesmes.org/xml/2002-08-01"
declares that any child tag whose name is prefixed by gesmes
, e.g.:
<gesmes:subject>
...
</gesmes:subject>
will actually have a tag name that incorporates the specified url into the tag name, something like this:
<http://www.gesmes.org/xml/2002-08-01:subject>
...
</http://www.gesmes.org/xml/2002-08-01:subject>
The reason you would want to use a namespace is to create a unique name for the Cube tag, so that it doesn't clash with another xml document's Cube tag.
The second namespace declaration:
xmlns="http://www.ecb.int/vocabulary/2002-08-01/eurofxref"
is a default namespace declaration. It declares that any child tag that does not specify a prefix will have the specified url incorporated into its tag name. So a tag like this:
<Cube>
...
</Cube>
becomes something like this:
<http://www.ecb.int/vocabulary/2002-08-01/eurofxref:Cube>
...
</http://www.ecb.int/vocabulary/2002-08-01/eurofxref:Cube>
However, it would be unwieldy to have to write a tag name like that in your xpaths, so in place of the url you instead use the shortcut xmlns
:
/xmlns:Cube
XML Namespace issue with Nokogiri
Because Nokogiri requires you to register the XML namespaces you are querying within (read more about XML Namespaces). But you should still be able to query the element if you specify its namespace when calling at_css
. To see the exact usage, check out the css
method documentation. It should end up looking something like this:
document.at_css "world", 'namespace_name' => 'namespace URI'
Avoiding Nokogiri::XML::XPath::SyntaxError: ERROR: Undefined namespace prefix
I ended up solving the problem by editing the XML file and adding the namespaces in the root. Here is an example:
temp = Nokogiri::XML(@document_xml)
temp.root['xmlns:w'] = "http://schemas.openxmlformats.org/wordprocessingml/2006/main"
@doc = Nokogiri::XML(temp.to_xml(:save_with => Nokogiri::XML::Node::SaveOptions::AS_XML))
Related Topics
Ruby Dsl (Domain Specific Language) Repositories, Examples
How to Wrap the Invocation of a Ruby Method by Including a Module
Rails 3. How to Add a Helper That Activeadmin Will Use
How to Set the Actionmailer Default_Url_Options's :Host Dynamically to the Request's Hostname
How to Remove a Substring After a Certain Character in a String Using Ruby
Ruby Modules and Classes Same Name in Structure
Difference Between Kernel#Yield_Self, Yield(Self), Kernel#Then and Object#Tap in Ruby
Replacing the 'Auto_Link' Method in Ruby on Rails 3.1
Rails Assets Pipeline "Cannot Allocate Memory - Nodejs"
How to Add a Virtual Attribute to a Model in Ruby on Rails
Any Way to Determine Which Object Called a Method
Importing CSV Quoting Error Is Driving Me Nuts
Error Running '_Rvm_Make Install'