Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Should namespaces be passed down to child elements? #1469

Closed
IceDragon200 opened this issue May 17, 2016 · 6 comments
Closed

Should namespaces be passed down to child elements? #1469

IceDragon200 opened this issue May 17, 2016 · 6 comments

Comments

@IceDragon200
Copy link

IceDragon200 commented May 17, 2016

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
@akostadinov
Copy link

Is this same as #425 ?

@IceDragon200
Copy link
Author

@akostadinov Yes, it appears so, I'm surprised this issue has been brought up before...

@CharlesP
Copy link

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

@orhantoy
Copy link
Contributor

orhantoy commented Feb 1, 2017

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>

@eugenebolshakov
Copy link

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>

@flavorjones
Copy link
Member

Apologies for not responding on this issue before now. This was originally reported at #425 and was fixed in #1712 for v1.12.0, but then modified in #2320 for v1.12.4.

Current behavior is controlled by Document#namespace_inheritance described at:

which defaults to false for normal documents, but defaults to true for Builder-generated documents. You can opt out of this by passing namespace_inheritance: false to the Builder constructure:

b = Nokogiri::XML::Builder.new(encoding: 'utf-8', namespace_inheritance: false) 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

b.to_xml
# => "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n" +
#    "<soapenv:Envelope xmlns:soapenv=\"http://schemas.xmlsoap.org/soap/envelope/\" xmlns:emer=\"http://dashcs.com/api/v1/emergency\">\n" +
#    "  <soapenv:Header/>\n" +
#    "  <soapenv:Body>\n" +
#    "    <emer:validateLocation>\n" +
#    "      <location>\n" +
#    "        <address1>Some place over the rainbow</address1>\n" +
#    "        <community>Community</community>\n" +
#    "        <postalcode>PS</postalcode>\n" +
#    "        <state>AR</state>\n" +
#    "        <type>ADDRESS</type>\n" +
#    "      </location>\n" +
#    "    </emer:validateLocation>\n" +
#    "  </soapenv:Body>\n" +
#    "</soapenv:Envelope>\n"

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

6 participants