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 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 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 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 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 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 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
Rectangle 27 0

Which means you need to put an underscore after the tag name.

Just to clarify with an example for anyone else who might be searching for this (such as I was):

builder = Nokogiri::XML::Builder.new do |xml|
  xml.questions {
    xml.question {
      xml.text_ "What is your name?"
    }
    xml.question {
      xml.text_ "What is your favourite colour?"
    }
  }
end

ruby - How do I use Nokogiri XML Builder with a field that is the word...

ruby xml nokogiri
Rectangle 27 0

Your example also takes a long time to execute here. And you were right: it's the garbage collector that's taking so long to execute. Try this:

require 'nokogiri'
class A
  def a
    builder = Nokogiri::XML::Builder.new do |xml|
      0.upto(81900) do
        xml.text "test"
      end
    end
  end
end
A.new.a
puts "End1"
GC.start
puts "End2"

Here, the delay happens between "End1" and "End2". After "End2" is printed, the program closes immediately.

Notice that I created an object to demonstrate it. Otherwise, the data generated by the builder can only be garbage collected when the program finishes.

As for the best way to do what you're trying to accomplish, I suggest you ask another question giving details of what exactly you're trying to do with the XML files.

Thank you for your answer, if I'll have enough reputation I'll gladly vote up that answer. Since I was aiming to find exactly what a problem is or find solution, this is an accepted answer. Thank you again for your time.

Glad to help. By the way, as far as I know voting up questions or answers has no negative effect on your reputation points. In fact, it will even get you badges sometimes :)

Yeah only downvotes costs you reputation. Upvotes are free and actually helps the whole community to detect great posts.

Well I absolutely agree with both of you, but unfortunately it is impossible to give a vote up because I don't have 15 reputation points yet :-) But when I will....like someone said before...I'll be back.

Ha! I didn't know you needed a minimum amount of points to bless others with upvotes :P

ruby - Nokogiri builder performance on huge XML? - Stack Overflow

ruby xml nokogiri builder
Rectangle 27 0

b = Nokogiri::XML::Builder.new do |xml|
  xml.root('xmlns:scp' => 'http://scp.com/') do
    xml['scp'].price '25', :unit => 'USD'
  end
end

ruby - How to build 25 in Nokogiri X...

ruby xml nokogiri
Rectangle 27 0

Bumped into this two years after the fact, and thought the accepted answer was way more convoluted than it needs to be.

The problem is, you're building an XML fragment here, while by default Nokogiri's builder assumes you're building a complete document. XML documents only have one root node.

One option would be to build the complete document as follows, then grab the root's children:

<DISPOSABLE_OUTER_NODE>
  <Data>
    <Groups>
      <GroupType>test</GroupType>
     </Groups>
  </Data>
  <AnotherNode>13</AnotherNode>
</DISPOSABLE_OUTER_NODE>

But there's a better, "fragmentary" way. You build an empty fragment, then have builder work on that rather than a new document:

frag = Nokogiri::XML::DocumentFragment.parse("")

Nokogiri::XML::Builder.with( frag ){ |b|
  b.Data do
    b.Groups do
      b.GroupType "test"
    end
  end

  b.AnotherNode "13"
}

puts frag.to_xml

Then just stick it into whatever document you're modifying.

Maybe with is a latecomer to Nokogiri. But it's an elegant solution to a problem I was having, so I thought it belonged here.

ruby - Add elements to Nokogiri XML Builder outside of nesting - Stack...

ruby xml nokogiri
Rectangle 27 0

Your code produces the following XML, which seems to meet your specifications. It doesn't produce an empty oneChild, at any rate. If this isn't what you're looking for, can you tell us what your ideal output would be?:

builder = Nokogiri::XML::Builder.new(:encoding => 'UTF-8') do |xml|
  xml.myRoot do |xml|
    xml.oneChild
    xml.anotherChild
  end
end

puts builder.to_xml

# <?xml version="1.0" encoding="UTF-8"?>
# <myRoot>
#   <oneChild/>
#   <anotherChild/>
# </myRoot>   

node = builder.doc.xpath('//myRoot/oneChild').first
Nokogiri::XML::Builder.with(node) do |xml|
  xml.childOfOneChild 'Im a child of oneChild'
end

puts builder.to_xml

# <?xml version="1.0" encoding="UTF-8"?>
# <myRoot>
#   <oneChild>
#     <childOfOneChild>Im a child of oneChild</childOfOneChild>
#   </oneChild>
#   <anotherChild/>
# </myRoot>

ruby - Append elements using Nokogiri::XML::Builder - Stack Overflow

ruby xml nokogiri
Rectangle 27 0

doc.to_xml :save_with => Nokogiri::XML::Node::SaveOptions::NO_DECLARATION

ruby - Is it possible to omit the processing instruction from an XML d...

ruby xml nokogiri
Rectangle 27 0

You can accomplish that by using an additional builder.

xml['soapenv'].Body do
  xml << Nokogiri::XML::Builder.new do |request_xml|
    xml.Request do
      request_xml.version        "1.1"
      request_xml.name           @admin_name.name
      request_xml.source_version "1.0"
      request_xml.downloadmarked "0"
      request_xml.from           @dateFrom
      request_xml.time_from      "0000"
      request_xml.to             @dateTo
      request_xml.time_to        "2359"
      request_xml.limit          "100"
    end
  end.doc.root.to_xml
end
<soapenv:Body>
  <Request>
    <version>1.1</version>
    <name>COMPANY NAME HERE</name>
    <source_version>1.0</source_version>
    <downloadmarked>0</downloadmarked>
    <from>20140125</from>
    <time_from>0000</time_from>
    <to>20140125</to>
    <time_to>2359</time_to>
    <limit>100</limit>
  </Request>
</soapenv:Body>

Using the << operator appends raw string to the document. Also note the use of #doc.root, if you just use #to_xml you'll get <?xml version="1.0"?> at the beginning of the string.

However if Request was desired to be namespaced but not it's children this approach wouldn't be ideal because you'd have to use a builder for each child (a builder can have only 1 root.) A solution for multiple "roots" is to use a DocumentFragment.

xml['soapenv'].Body do
  request = Nokogiri::XML::DocumentFragment.parse ""

  Nokogiri::XML::Builder.with(request) do |request_xml|
    request_xml.version        "1.1"
    request_xml.name           @admin_name.name
    request_xml.source_version "1.0"
    request_xml.downloadmarked "0"
    request_xml.from           @dateFrom
    request_xml.time_from      "0000"
    request_xml.to             @dateTo
    request_xml.time_to        "2359"
    request_xml.limit          "100"
  end

  xml.Request << request.to_xml
end
<soapenv:Body>
  <soapenv:Request>
    <version>1.1</version>
    <name>COMPANY NAME HERE</name>
    <source_version>1.0</source_version>
    <downloadmarked>0</downloadmarked>
    <from>20140125</from>
    <time_from>0000</time_from>
    <to>20140125</to>
    <time_to>2359</time_to>
    <limit>100</limit>
  </soapenv:Request>
</soapenv:Body>

ruby - How do I not inherit namespace from parent element using Nokogi...

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

When you're setting the text of an element, you really are setting text, not HTML source. < and & don't have any special meaning in plain text.

So just type a bullet: ''. Of course your source code and your XML file will have to be using the same encoding for that to come out right. If your XML file is UTF-8 but your source code isn't, you'd probably have to say '\xe2\x80\xa2' which is the UTF-8 byte sequence for the bullet character as a string literal.

(In general non-ASCII characters in Ruby 1.8 are tricky. The byte-based interfaces don't mesh too well with XML's world of all-text-is-Unicode.)

Why do you need that particular escaped version? If you are having encoding troubles so the doesn't appear like you type it, then you should try to fix those by setting your encoding right rather than resorting to HTML-escapes. (Whilst in other environments you might ask your HTML serialiser to escape all non-ASCII characters to HTML-ampersand-sequences to get around this, Ruby doesn't currently have that level of Unicode support.)

Adding non-escaped Ampersands to HTML with Nokogiri::XML::Builder - St...

html xml utf-8 escaping nokogiri
Rectangle 27 0

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