Skip to content

Instantly share code, notes, and snippets.

@mudge
Created June 7, 2010 09:29
Show Gist options
  • Save mudge/428455 to your computer and use it in GitHub Desktop.
Save mudge/428455 to your computer and use it in GitHub Desktop.
Namespaces with Nokogiri::Builder
# Dealing with namespaces in Nokogiri.
#
# First thing you must do is abandon what you're used to with Builder,
# namespaces aren't just attributes on an element, they uniquely identify
# what sort of element you are building and, as such, you are better off
# specifying them manually.
#
# The key here is accessing the Nokogiri::XML::Element being built with
# b.parent, then you can set the namespace (distinguished by an element
# with a prefix such as "soap12:Envelope") or add a default namespace
# (where there is no prefix but an "xmlns" attribute).
#
# The example below shows both types of namespaces being created and used
# including setting a namespace on the root element of a document.
#
# Thanks to http://stackoverflow.com/questions/1829425/creating-an-xml-document-with-a-namespaced-root-element-with-nokogiri-builder
# for pointing me in the right direction.
b = Nokogiri::XML::Builder.new
b.Envelope("xmlns:xsi" => "http://www.w3.org/2001/XMLSchema-instance", "xmlns:xsd" => "http://www.w3.org/2001/XMLSchema") {
b.parent.namespace = b.parent.add_namespace_definition("soap12", "http://www.w3.org/2003/05/soap-envelope")
b['soap12'].Body {
b.Foo {
b.parent.default_namespace = "http://FOO/Bar"
b.exampleXML {
b.example("xsi:schemaLocation" => "http://www.example.com/schemas https://service.example.com/FOO/schemas/monkey.xsd") {
b.parent.add_namespace_definition("xsi", "http://www.w3.org/2001/XMLSchema-instance")
b.parent.default_namespace = "http://www.example.com/schemas"
b.dummy("Test")
b.domain("woo")
b.person {
b.name("test")
}
}
}
}
}
}
# This will create the following XML:
#
# <?xml version="1.0"?>
# <soap12:Envelope xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:soap12="http://www.w3.org/2003/05/soap-envelope">
# <soap12:Body>
# <Foo xmlns="http://FOO/Bar">
# <exampleXML>
# <example xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://www.example.com/schemas" xsi:schemaLocation="http://www.example.com/schemas https://service.example.com/FOO/schemas/monkey.xsd">
# <dummy>Test</dummy>
# <domain>woo</domain>
# <person>
# <name>test</name>
# </person>
# </example>
# </exampleXML>
# </Foo>
# </soap12:Body>
# </soap12:Envelope>
#
# Now that it's all namespaced properly, don't forget how to select
# elements with XPath: http://tenderlovemaking.com/2009/04/23/namespaces-in-xml/
#
# n = Nokogiri::XML(b.to_xml)
# n.xpath("//n:Foo", "xmlns:n" => "http://FOO/Bar")
# n.xpath("//n:dummy", "xmlns:n" => "http://www.example.com/schemas")
# n.xpath("//n:person/n:name", "xmlns:n" => "http://www.example.com/schemas")
@stuzero
Copy link

stuzero commented Jan 21, 2014

I don't think you really need the "parent" hack. Check this out:

require 'nokogiri'

namespaces = {"xmlns:soapenv" => "http://schemas.xmlsoap.org/soap/envelope/", "xmlns:api" => "http://api.example.com/"}

b = Nokogiri::XML::Builder.new 

b[:soapenv].Envelope(namespaces) {
  b[:soapenv].Body  { 
    b[:api].ClientLogin() {
      b[:api].username('MY_USERNAME')
      b[:api].password('MY_PASSWORD')
      b[:api].applicationKey('MY_APP_KEY')
    }
  }
}

it creates this xml:

<?xml version="1.0"?>
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/"xmlns:api="http://api.example.com/">
  <soapenv:Body>
    <api:ClientLogin>
      <api:username>MY_USERNAME</api:username>
      <api:password>MY_PASSWORD</api:password>
      <api:applicationKey>MY_APP_KEY</api:applicationKey>
    </api:ClientLogin>
  </soapenv:Body>
</soapenv:Envelope>

@entrity
Copy link

entrity commented Feb 23, 2021

Love it, @stuzero !

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment