Adding namespace using Nokogiri's XML Builder

前端 未结 2 943
天涯浪人
天涯浪人 2020-12-15 13:17

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 structu

相关标签:
2条回答
  • 2020-12-15 13:51

    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
    
    0 讨论(0)
  • 2020-12-15 14:04
    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.

    0 讨论(0)
提交回复
热议问题