I'm having a bit of a problem, I may be overlooking something quite obvious, but here is the gist of my problem https://gist.github.com/IceDragon200/873139e6c7d395dfcd9793949f9b2246
What's happening here, I've created an element with a namespace and then child elements without a namespace, expecting the children to be as such (namespace-less), however nokogiri slaps the label on anyway.
Expected behaviour:
# WHERE xml is the top level builder
xml['namespace'].element do |builder_with_namespace|
xml.element_without_namespace
builder_with_namespace.element_with_namespace
end
Though judging from that, it wouldn't exactly be efficient, since you'd create a new builder context per element, unless checking the block's arity to avoid it.
tl;dr
Expected
<?xml version="1.0" encoding="utf-8"?>
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:emer="http://dashcs.com/api/v1/emergency">
<soapenv:Header/>
<soapenv:Body>
<emer:validateLocation>
<location>
<address1>Some place over the rainbow</address1>
<community>Community</community>
<postalcode>PS</postalcode>
<state>AR</state>
<type>ADDRESS</type>
</location>
</emer:validateLocation>
</soapenv:Body>
</soapenv:Envelope>
Actual
<?xml version="1.0" encoding="utf-8"?>
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:emer="http://dashcs.com/api/v1/emergency">
<soapenv:Header/>
<soapenv:Body>
<emer:validateLocation>
<emer:location>
<emer:address1>Some place over the rainbow</emer:address1>
<emer:community>Community</emer:community>
<emer:postalcode>PS</emer:postalcode>
<emer:state>AR</emer:state>
<emer:type>ADDRESS</emer:type>
</emer:location>
</emer:validateLocation>
</soapenv:Body>
</soapenv:Envelope>
Test
require 'nokogiri' # nokogiri (1.6.7.2)
result = Nokogiri::XML::Builder.new(encoding: 'utf-8') do |xml|
xml['soapenv'].Envelope('xmlns:soapenv' => 'http://schemas.xmlsoap.org/soap/envelope/', 'xmlns:emer' => 'http://dashcs.com/api/v1/emergency') do
xml['soapenv'].Header
xml['soapenv'].Body do
xml['emer'].validateLocation do
# these do not require a namespace
xml.location do
xml.address1 'Some place over the rainbow'
xml.community 'Community'
xml.postalcode 'PS'
xml.state 'AR'
xml.type 'ADDRESS'
end
end
end
end
end.to_xml
# suddenly WILD NAMESPACES APPEAR!
puts result
Is this same as #425 ?
@akostadinov Yes, it appears so, I'm surprised this issue has been brought up before...
For a temporary workaround, have you tried explicitly setting the namespace nil?
The following is working for me:
result = Nokogiri::XML::Builder.new(encoding: 'utf-8') do |xml|
xml['soapenv'].Envelope('xmlns:soapenv' => 'http://schemas.xmlsoap.org/soap/envelope/', 'xmlns:emer' => 'http://dashcs.com/api/v1/emergency') do
xml['soapenv'].Header
xml['soapenv'].Body do
xml['emer'].validateLocation do
xml.parent.namespace = nil # explicitly set namespace to nil
xml.location do
xml.address1 'Some place over the rainbow'
xml.community 'Community'
xml.postalcode 'PS'
xml.state 'AR'
xml.type 'ADDRESS'
end
end
end
end
end.to_xml
I had the same problem but setting the parent namespace to nil did not work for me.
This is what I initially did
Nokogiri::XML::Builder.new { |xml| xml["foo"].Root("xmlns:foo" => "localhost") { xml.Test "hello" } }.to_xml
<?xml version="1.0"?>
<foo:Root xmlns:foo="localhost">
<foo:Test>hello</foo:Test>
</foo:Root>
But I only wanted the namespace on Root.
I then found this GH issue and tried the following
Nokogiri::XML::Builder.new { |xml| xml["foo"].Root("xmlns:foo" => "localhost") { xml.parent.namespace = nil; xml.Test "hello" } }.to_xml
<?xml version="1.0"?>
<Root xmlns:foo="localhost">
<Test>hello</Test>
</Root>
The namespace then got removed from both Root and Test.
Ultimately I got the desired output by introducing a default namespace
Nokogiri::XML::Builder.new { |xml| xml["foo"].Root("xmlns:foo" => "localhost", "xmlns" => "default") { xml.Test "hello" } }.to_xml
<?xml version="1.0"?>
<foo:Root xmlns:foo="localhost" xmlns="default">
<Test>hello</Test>
</foo:Root>
BUT the XML endpoint I was calling didn't want the default namespace, 馃槥 , soooo...
Nokogiri::XML::Builder
.new { |xml| xml["foo"].Root("xmlns:foo" => "localhost", "xmlns" => "default") { xml.Test "hello" } }
.to_xml
.sub(' xmlns="default"', "")
<?xml version="1.0"?>
<foo:Root xmlns:foo="localhost">
<Test>hello</Test>
</foo:Root>
The following workaround works if you need to keep the parent's namespace (set it to nil temporarily and then restore after you created all child elements that don't need a namespace):
require 'nokogiri'
result = Nokogiri::XML::Builder.new(encoding: 'utf-8') do |xml|
xml['soapenv'].Envelope('xmlns:soapenv' => 'http://schemas.xmlsoap.org/soap/envelope/', 'xmlns:emer' => 'http://dashcs.com/api/v1/emergency') do
xml['soapenv'].Header
xml['soapenv'].Body do
xml['emer'].validateLocation do
# these do not require a namespace
parent_namespace = xml.parent.namespace
xml.parent.namespace = nil
xml.location do
xml.address1 'Some place over the rainbow'
xml.community 'Community'
xml.postalcode 'PS'
xml.state 'AR'
xml.type 'ADDRESS'
end
xml.parent.namespace = parent_namespace
end
end
end
end.to_xml
puts result
Produces:
<?xml version="1.0" encoding="utf-8"?>
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:emer="http://dashcs.com/api/v1/emergency">
<soapenv:Header/>
<soapenv:Body>
<emer:validateLocation>
<location>
<address1>Some place over the rainbow</address1>
<community>Community</community>
<postalcode>PS</postalcode>
<state>AR</state>
<type>ADDRESS</type>
</location>
</emer:validateLocation>
</soapenv:Body>
</soapenv:Envelope>
Most helpful comment
The following workaround works if you need to keep the parent's namespace (set it to nil temporarily and then restore after you created all child elements that don't need a namespace):
Produces: