I have been wrecking my head for a few hours but I can't seem to determine how to add XMLNS namespace whilst using the Nokogiri XML Builder class to construct a XML structure.
For instance, consider the XML sample below: I can create everything between the GetQuote tags but creating the "p:ACMRequest" remains a mystery.
I came across this reference, https://gist.github.com/428455/7a15f84cc08c05b73fcec2af49947d458ae3b96a, that still doesn't make sense to me. Even referring to the XML documentation,http://www.w3.org/TR/xml-names/, didn't make much sense either.
<?xml version="1.0" encoding="UTF-8"?>
<p:ACMRequest xmlns:p="http://www.acme.com" xmlns:p1="http://www.acme.com/datatypes" xmlns:p2="http://www.acme.com/ACMRequestdatatypes" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.acme.com ACM-req.xsd ">
<GetQuote>
<Request>
<ServiceHeader>
...
...
</ServiceHeader>
</Request>
<From>
...
...
</From>
<Details>
...
...
</Details>
</GetQuote>
</p:ACMRequest>
The Nokogiri documentation page says:
Namespaces are added similarly to attributes.
Nokogiri::XML::Builder
assumes that when an attribute starts with “xmlns”, it is meant to be a namespace:builder = Nokogiri::XML::Builder.new { |xml| xml.root('xmlns' => 'default', 'xmlns:foo' => 'bar') do xml.tenderlove end } puts builder.to_xml
Will output XML like this:
<?xml version="1.0"?> <root xmlns:foo="bar" xmlns="default"> <tenderlove/> </root>
Applying this to your specific question, simply do:
require 'nokogiri'
NS = {
"xmlns:p" => "http://www.acme.com",
"xmlns:p1" => "http://www.acme.com/datatypes",
"xmlns:p2" => "http://www.acme.com/ACMRequestdatatypes",
"xmlns:xsi" => "http://www.w3.org/2001/XMLSchema-instance",
}
builder = Nokogiri::XML::Builder.new { |xml|
xml.ACMRequest(NS) do
xml.GetQuote
end
}
puts builder.to_xml
#=> <?xml version="1.0"?>
#=> <ACMRequest xmlns:p="http://www.acme.com" xmlns:p1="http://www.acme.com/datatypes" xmlns:p2="http://www.acme.com/ACMRequestdatatypes" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
#=> <GetQuote/>
#=> </ACMRequest>
As for the namespace prefix on the root element itself…
<p:ACMRequest xmlns:p="…">…</p:ACMRequest>
…I cannot figure out how to apply a namespace prefix to the first element in Nokogiri during creation. Instead, you have to apply the namespace after creating the document:
root = builder.doc.root
acme = root.namespace_definitions.find{ |ns| ns.href==NS["xmlns:p"] }
root.namespace = acme
puts builder.to_xml
#=> <?xml version="1.0"?>
#=> <p:ACMRequest xmlns:p="http://www.acme.com" xmlns:p1="http://www.acme.com/datatypes" xmlns:p2="http://www.acme.com/ACMRequestdatatypes" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">atypes" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
#=> <GetQuote/>
#=> </p:ACMRequest>
Alternatively, you can cheat:
# This happens to work for now, but I doubt you should rely upon it.
builder.doc.root.name = "p:ACMRequest"
Per "How to create an XML document with a namespaced root element with Nokogiri Builder" you can alternatively do this during creation via a small hack:
builder = Nokogiri::XML::Builder.new { |xml|
xml.ACMRequest(NS) do
xml.parent.namespace = … # find the ns in xml.parent.namespace_definitions
# …
end
end
require 'nokogiri'
NS = {
"xmlns:p" => "http://www.acme.com",
"xmlns:p1" => "http://www.acme.com/datatypes",
"xmlns:p2" => "http://www.acme.com/ACMRequestdatatypes",
"xmlns:xsi" => "http://www.w3.org/2001/XMLSchema-instance",
}
builder = Nokogiri::XML::Builder.new { |xml|
xml['p'].ACMRequest(NS) do
xml.GetQuote
end
}
puts builder.to_xml
Produces:
<?xml version="1.0"?>
<p:ACMRequest xmlns:p="http://www.acme.com" xmlns:p1="http://www.acme.com/datatypes" xmlns:p2="http://www.acme.com/ACMRequestdatatypes" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<p:GetQuote/>
</p:ACMRequest>
This is documented in the builder API: http://nokogiri.org/Nokogiri/XML/Builder.html:
Referencing declared namespaces
Tags that reference non-default namespaces (i.e. a tag “foo:bar”) can be built by using the Nokogiri::XML::Builder#[] method.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With