Rectangle 27 6

Two years later, I found a cleaner way to do this by using Nokogiri::XML::Builder's document reference to retrieve the root node and add the namespace(s) to that. Like the previous solution, it requires the root node to exist before the namespaces can be added to it.

<root>
<rootElement>
builder = Nokogiri::XML::Builder.new do |xml| 
  xml.rootElement do
    # create a namespace and save it for later
    ns = xml.doc.root.add_namespace_definition('foo', 'my-ns-url')
    # you can also create multiple name spaces
    xml.doc.root.add_namespace_definition('bar', 'http://example.com/bar')

    # assign the saved namespace to rootElement
    xml.doc.root.namespace = ns

    xml['foo'].child
    xml['bar'].child
  end
end
builder.to_xml
<?xml version="1.0"?>
<foo:rootElement xmlns:foo="my-ns-url" xmlns:bar="http://example.com/bar">
  <foo:child/>
  <bar:child/>
</foo:rootElement>

I like this better because you don't have to search for the name space, and it's easier to add multiple namespaces.

This works great. Thanks. However, I am unable to get rid of the namespace inheritance. I want to achieve something like the following. <?xml version="1.0"?> <foo:rootElement xmlns:foo="my-ns-url" xmlns:bar="example.com/bar" <child> value </child> </foo:rootElement>

ruby - Creating an XML document with a namespaced root element with No...

xml ruby namespaces nokogiri
Rectangle 27 10

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
<?xml version="1.0"?>
   <root xmlns:foo="bar" xmlns="default">
     <tenderlove/>
   </root>
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>
# This happens to work for now, but I doubt you should rely upon it.
builder.doc.root.name = "p:ACMRequest"
builder = Nokogiri::XML::Builder.new { |xml|
  xml.ACMRequest(NS) do
   xml.parent.namespace =  # find the ns in xml.parent.namespace_definitions
   # 
  end
end

@muistooshort No; the first four blocks (quote, code, quote, code) are all from the page. I'll indent them all for increased clarity.

It looked like an editing mistake but it was just a Markdown artifact. You can make a "code block inside a blockquote" work if you use enough leading spaces on the code block and enough > for the blockquote. I think I fixed it, I'm just feeling a bit OCD this morning

Sorry for the late response. @Phrogz thanks for your extensive response. My experience with XML has been rather limited but your response did clear up a few other questions I had as well. I have lost count of how many times I have looked at the Nokogiri documentation but its not till someone else explains it that things makes sense. :-) yup i did notice the typo in the code block too :-)

Congratulations, now the first hit on Google for that is this page.

ruby - Adding namespace using Nokogiri's XML Builder - Stack Overflow

ruby xml nokogiri xml-namespaces
Rectangle 27 6

Without using a private method you can get a handle on the current parent element using the parent method of the Builder instance. Then you can append an element to that (even from another document). For example:

require 'nokogiri'
doc1 = Nokogiri.XML('<r><a>success!</a></r>')
a = doc1.at('a')

# note that `xml` is not a Nokogiri::XML::Document,
#  but rather a Nokogiri::XML::Builder instance.
doc2 = Nokogiri::XML::Builder.new do |xml|
  xml.some do
    xml.more do
      xml.parent << a
    end
  end
end.doc

puts doc2
#=> <?xml version="1.0"?>
#=> <some>
#=>   <more>
#=>     <a>success!</a>
#=>   </more>
#=> </some>
#insert

ruby - Adding a XML Element to a Nokogiri::XML::Builder document - Sta...

ruby xml nokogiri
Rectangle 27 55

Using Nokogiri::XML::Reader works for your example, but probably isn't the full answer you are looking for (Note that there is no attributes method for Builder).

reader = Nokogiri::XML::Reader(builder.to_xml)
reader.read #Moves to next node in document
reader.attribute("messageId")

Note that if you issued reader.read again and then tried reader.attribute("messageId") the result will be nil since the current node will not have this attribute.

What you probably want to do is use Nokogiri::XML::Document if you want to search an XML document by attribute.

doc = Nokogiri::XML(builder.to_xml)
elems = doc.xpath("//*[@messageId]") #get all elements with an attribute of 'messageId'
elems[0].attr('messageId') #gets value of attribute of first elem

I looked @ this code dzone.com/snippets/finding-elements-attributes and was breaking my head because I was using the @ outside [. This has been a real saviour. I have wasted 2 days to figure out a way to parse xml attributes and this is a true saviour. It will be nice if this link added to nokogiri

nokogiri_element.xpath("@id").text()

ruby - How to access attributes using Nokogiri - Stack Overflow

ruby nokogiri builder
Rectangle 27 2

class Nokogiri::XML::Builder
    def entity(code)
      doc = Nokogiri::XML("<?xml version='1.0'?><root>&##{code};</root>")
      insert(doc.root.children.first)
    end
  end
builder = Nokogiri::XML::Builder.new do |xml|
    xml.span {
      xml.text "I can has "
      xml.entity 8665
      xml.text " entity?"
    }
  end
  puts builder.to_xml
<?xml version="1.0"?>
<span>I can has &#x2022; entity?</span>

PS this a workaround only, for a clean solution please refer to the libxml2 documentation (Nokogiri is built on libxml2) for more help. However, even these folks admit that handling entities can be quite ..err, cumbersome sometimes.

thanks adrian, what is an "entity", and where'd you get 8665?

if I do 8226 instead of 8665, it parses it to "bull;" :/

<!ENTITY bull CDATA "&#8226;" -- bullet, =black small circle, u+2022 ISOpub -->

How to add non-escaped ampersands to HTML with Nokogiri::XML::Builder ...

html xml utf-8 escaping nokogiri
Rectangle 27 8

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
<?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>

Tags that reference non-default namespaces (i.e. a tag foo:bar) can be built by using the Nokogiri::XML::Builder#[] method.

This works fine with nokogiri 1.6.1, but not 1.5.6.

ruby - Adding namespace using Nokogiri's XML Builder - Stack Overflow

ruby xml nokogiri xml-namespaces
Rectangle 27 36

Here is a slightly more succinct way to access attributes using Nokogiri (assuming you already have your xml stored in a variable called xml, as covered by @atomicules' answer):

xml.xpath("//Placement").attr("messageId")

ruby - How to access attributes using Nokogiri - Stack Overflow

ruby nokogiri builder
Rectangle 27 13

require 'rubygems'
require 'nokogiri'

puts Nokogiri::XML::Builder.new { |xml| 
  xml.root("xmlns:foo"=>"url") {
    xml.parent.namespace = xml.parent.namespace_definitions.find{|ns|ns.prefix=="foo"}
    xml['foo'].child
  }
}.to_xml

You cannot use xml['foo'] before the namespace is defined, ie before you pass it as an argument to the root node. Thus, the code above add the namespace after-the-fact to the root node.

Works like a charm. Thanks!

Note that this is fine if there's only one namespace, or maybe if you're working under Ruby 1.9 and thus can rely on the order of the hash arguments to be the same. Otherwise, I would recommend xml.parent.namespace = xml.parent.namespace_definitions.find{ |ns| ns.prefix=="foo" } (or use the ns.href if you're not sure what the prefix might have been named).

ruby - Creating an XML document with a namespaced root element with No...

xml ruby namespaces nokogiri
Rectangle 27 5

To generate an xml file you do not need Rails. Ruby (and Nokogiri or some oder builder) will suffice.

First, you have to build your xml:

builder = Nokogiri::XML::Builder.new(:encoding => 'utf-8') do |xml|
  xml.root do
    xml.products do
      xml.widget do
        xml.id_ "10"
        xml.name "Awesome widget"
      end
    end
  end
end

You can get an xml string from this builder using builder.to_xml:

xml_string = builder.to_xml # => "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<root>\n  <products>\n    <widget>\n      <id>10</id>\n      <name>Awesome widget</name>\n    </widget>\n  </products>\n</root>\n"

To save this string into a file, you use File.open and File#write:

# open a file instance with path '/path/to/file.xml' in write mode (-> 'w')
File.open('/path/to/file.xml', 'w') do |file|
  # write the xml string generated above to the file
  file.write xml_string
end

An important note: you always have to make sure that you close all files that you opened if you no longer need them. File.open with a block (as used in my snipped above) will automatically close the file for you after the code in the block is executed...

ruby on rails - Nokogiri XML file output is located where? - Stack Ove...

ruby-on-rails ruby xml nokogiri
Rectangle 27 39

require 'nokogiri'

b = Nokogiri::XML::Builder.new do |xml|
  xml.send(:"fooo-bar", "hello")
end

puts b.to_xml

where does the hello come in? xml.send(:"foo-bar", "hello")?

Where is it documented in official Nokogiri documentation? can you please share a link?

Bit late to the party here, but that :"xx-aaa" syntax is the standard Ruby way of making a symbol when the syntax won't work for you

ruby - How do I create XML using Nokogiri::XML::Builder with a hyphen ...

xml ruby nokogiri
Rectangle 27 26

Bart Vandendriessche's answer works but there is a simpler solution if you only want a text field within the element.

If you need them to be nested then you can pass a block

require 'nokogiri'

b = Nokogiri::XML::Builder.new do |xml|
  xml.send(:'foo-bar') {
    xml.send(:'bar-foo', 'hello')
  }
end

puts b.to_xml
<?xml version="1.0"?>
<foo-bar>
  <bar-foo>hello</bar-foo>
</foo-bar>

ruby - How do I create XML using Nokogiri::XML::Builder with a hyphen ...

xml ruby nokogiri
Rectangle 27 2

Try adding the attribute xmlns="", which seems to hint to the XML builder that elements should be in the default namespace unless otherwse declared. I believe the resulting document is semantically equivalent to your example despite its presence...

attrs = {
  'xmlns' => '',
  'xmlns:req' => 'http://www.google.com',
  'xmlns:xsi' => 'http://www.w3.org/2001/XMLSchema-instance',
  'schemaVersion' => '1.0',
}
builder = Nokogiri::XML::Builder.new do |xml|
  xml['req'].Request(attrs) {
    xml.LanguageCode('en')
    xml.Enabled('Y')
  }
end

builder.to_xml # =>
# <?xml version="1.0"?>
# <req:Request xmlns="" xmlns:req="http://www.google.com" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" schemaVersion="1.0">
#   <LanguageCode>en</LanguageCode>
#   <Enabled>Y</Enabled>
# </req:Request>

I think you need 'xmlns' => '' (i.e. an empty string rather than the string default). The spec says The attribute value in a default namespace declaration MAY be empty. This has the same effect, within the scope of the declaration, of there being no default namespace. Using default will put all unprefixed elements into the namespace named default, not no namespace.

@matt: hmm, makes sense. I've updated the answer.

ruby - Add prefix to XML root node - Stack Overflow

ruby xml nokogiri xml-builder
Rectangle 27 3

After looking at the Nokogiri source I have found this fragile solution: using the protected #insert(node) method.

The code, modified to use that private method looks like this:

doc.another {
    xml_text = node.to_xml(:skip_instruct => true).to_s
    doc.send('insert', xml_text) # <= use `#insert` instead of `<<`

    doc.second("hi")
}

ruby - Adding a XML Element to a Nokogiri::XML::Builder document - Sta...

ruby xml nokogiri
Rectangle 27 3

Aaron Patterson's answer is correct and will work for element names containing any character that may otherwise be interpreted by the Ruby parser.

Answering Angela's question: to place text inside a element created this way you can do something like this:

require 'rubygems'
require 'nokogiri'

b = Nokogiri::XML::Builder.new do |xml|
  xml.send(:'foo.bar') {
    xml.text 'hello'
  }
end

puts b.to_xml

ruby - How do I create XML using Nokogiri::XML::Builder with a hyphen ...

xml ruby nokogiri
Rectangle 27 1

builder = Nokogiri::XML::Builder.new do  |xml|
  xml.send("foo:bar") do
  end
end


?> puts builder.to_xml
<?xml version="1.0"?>
<foo:bar/>

ruby on rails - How can I use a colon : in a nokogiri node name? - Sta...

ruby-on-rails ruby xml ruby-on-rails-3 nokogiri
Rectangle 27 0

builder = Nokogiri::XML::Builder.new do |b|
  b.html do
    b.head do
      b << stylesheet_link_tag 'style'
    end
  end
end
builder.to_xml

ruby on rails - Preventing Nokogiri from escaping characters? - Stack ...

ruby-on-rails ruby nokogiri
Rectangle 27 0

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
<?xml version="1.0"?>
   <root xmlns:foo="bar" xmlns="default">
     <tenderlove/>
   </root>
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>
# This happens to work for now, but I doubt you should rely upon it.
builder.doc.root.name = "p:ACMRequest"
builder = Nokogiri::XML::Builder.new { |xml|
  xml.ACMRequest(NS) do
   xml.parent.namespace =  # find the ns in xml.parent.namespace_definitions
   # 
  end
end

@muistooshort No; the first four blocks (quote, code, quote, code) are all from the page. I'll indent them all for increased clarity.

It looked like an editing mistake but it was just a Markdown artifact. You can make a "code block inside a blockquote" work if you use enough leading spaces on the code block and enough > for the blockquote. I think I fixed it, I'm just feeling a bit OCD this morning

Sorry for the late response. @Phrogz thanks for your extensive response. My experience with XML has been rather limited but your response did clear up a few other questions I had as well. I have lost count of how many times I have looked at the Nokogiri documentation but its not till someone else explains it that things makes sense. :-) yup i did notice the typo in the code block too :-)

ruby - Adding namespace using Nokogiri's XML Builder - Stack Overflow

ruby xml nokogiri xml-namespaces
Rectangle 27 0

require 'nokogiri'

b = Nokogiri::XML::Builder.new do |xml|
  xml.send(:"fooo-bar", "hello")
end

puts b.to_xml

where does the hello come in? xml.send(:"foo-bar", "hello")?

Where is it documented in official Nokogiri documentation? can you please share a link?

ruby - How do I create XML using Nokogiri::XML::Builder with a hyphen ...

xml ruby nokogiri
Rectangle 27 0

Without using a private method you can get a handle on the current parent element using the parent method of the Builder instance. Then you can append an element to that (even from another document). For example:

require 'nokogiri'
doc1 = Nokogiri.XML('<r><a>success!</a></r>')
a = doc1.at('a')

# note that `xml` is not a Nokogiri::XML::Document,
#  but rather a Nokogiri::XML::Builder instance.
doc2 = Nokogiri::XML::Builder.new do |xml|
  xml.some do
    xml.more do
      xml.parent << a
    end
  end
end.doc

puts doc2
#=> <?xml version="1.0"?>
#=> <some>
#=>   <more>
#=>     <a>success!</a>
#=>   </more>
#=> </some>
#insert

ruby - Adding a XML Element to a Nokogiri::XML::Builder document - Sta...

ruby xml nokogiri
Rectangle 27 0

Here is a possible solution, though it looks like a dirty trick:

#1. I build the svg document
builder = Nokogiri::XML::Builder.new do |xml|
  xml.svg do
    # ...
  end
end

#2. I retrieve the svg root node
svg = builder.doc.xpath("/svg").first

#3. I define and parse an xml document with the required preamble and dtd
str =<<EOS
<?xml version="1.0" standalone="no"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1. /DTD/svg11.dtd">
EOS
doc = Nokogiri::XML::Document.parse(str)

#4. I add the svg node to the document above
doc.add_child(svg)

xml - Add a dtd using nokogiri builder - Stack Overflow

xml ruby nokogiri dtd