How to generate XML file using Ruby and Builder::XMLMarkup templates? - ruby

As you all know, with Rails it is possible to use Builder::XMLMarkup templates to provide an http reponse in XML format instead of HTML (with the respond_to command). My problem is that I would like to use the Builder::XMLMarkup templating system not with Rails but with Ruby only (i.e. a standalone program that generates/outputs an XML file from an XML template). The question is then twofold:
How do I tell the Ruby program which is the template I want to use? and
How do I tell the Builder class which is the output XML file ?
There is already a similar answer to that in Stackoverflow (How do I generate XML from XMLBuilder using a .xml.builder file?), but I am afraid it is only valid for Rails.

Here's a simple example showing the basics:
require 'builder'
#received_data = {:books => [{ :author => "John Doe", :title => "Doeisms" }, { :author => "Jane Doe", :title => "Doeisms II" }]}
#output = ""
xml = Builder::XmlMarkup.new(:target => #output, :indent => 1)
xml.instruct!
xml.books do
#received_data[:books].each do |book|
xml.book do
xml.title book[:title]
xml.author book[:author]
end
end
end
The #output object will contain your xml markup:
<?xml version="1.0" encoding="UTF-8"?>
<books>
<book>
<title>Doeisms</title>
<author>John Doe</author>
</book>
<book>
<title>Doeisms II</title>
<author>Jane Doe</author>
</book>
</books>
The Builder docs at github.com provide more examples and links to more documentation.
To select a specific template, you could pass arguments to your program for this decision.
Anyway, I prefer to use libxml-ruby to parse and build XML documents, but that's a matter of taste.

I used Tilt to do (the first part of) this. It's really easy:
require 'builder'
require 'tilt'
template = Tilt.new('templates/foo.builder')
output = template.render
That will get you a string representation of your xml. At that point you can write it out to disk yourself.

Related

How do I select an attribute from a Nokogiri::XML.parse result set element [duplicate]

I have a simple task of accessing the values of some attributes. This is a simple script that uses Nokogiri::XML::Builder to create a simple XML doc.
require 'nokogiri'
builder = Nokogiri::XML::Builder.new(:encoding => 'UTF-8') do |xml|
xml.Placement(:messageId => "392847-039820-938777", :system => "MOD", :version => "2.0") {
xml.objects {
xml.object(:myattribute => "99", :anotherattrib => "333")
xml.nextobject_ '9387toot'
xml.Entertainment "Last Man Standing"
}
}
end
puts builder.to_xml
puts builder.root.attributes["messageId"]
The results are:
<?xml version="1.0" encoding="UTF-8"?>
<Placement messageId="392847-039820-938777" version="2.0" system="MOD">
<objects>
<object anotherattrib="333" myattribute="99"/>
<nextobject>9387toot</nextobject>
<Entertainment>Last Man Standing</Entertainment>
</objects>
</Placement>
C:/Ruby/lib/ruby/gems/1.8/gems/nokogiri-1.4.2-x86-mingw32/lib/nokogiri/xml/document.rb:178:in `add_child': Document already has a root node (RuntimeError)
from C:/Ruby/lib/ruby/gems/1.8/gems/nokogiri-1.4.2-x86-mingw32/lib/nokogiri/xml/node.rb:455:in `parent='
from C:/Ruby/lib/ruby/gems/1.8/gems/nokogiri-1.4.2-x86-mingw32/lib/nokogiri/xml/builder.rb:358:in `insert'
from C:/Ruby/lib/ruby/gems/1.8/gems/nokogiri-1.4.2-x86-mingw32/lib/nokogiri/xml/builder.rb:350:in `method_missing'
from C:/Documents and Settings/etrojan/workspace/Lads/tryXPATH2.rb:15
The XML that is generated looks fine. However, my attempts to access attributes cause an error to be generated:
Document already has a root node
I don't understand why puts would cause this error.
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
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")

Nokogiri::XML::Builder: Need to use the string "send" as element name

I am writing an application to generate XML files as input to SipP.
One tag frequently used by SipP is 'send'
The problem is, when I use nokogiri to build the xml for me
builder = Nokogiri::XML::Builder.new do |xml|
xml.send "Some Content"
end
I get this
<?xml version="1.0"?>
<Some Content/>
The same happens when I do this:
builder = Nokogiri::XML::Builder.new do |xml|
xml.send(:'send', "Some Content")
end
I can't spell 'SEND' in capital letters, because SipP won't understand it that way.
Any ideas how to force nokogiri to create an element with the name 'send'?
Thank you
From the docs:
The builder works by taking advantage of method_missing. Unfortunately
some methods are defined in ruby that are difficult or dangerous to
remove. You may want to create tags with the name “type”, “class”, and
“id” for example. In that case, you can use an underscore to
disambiguate your tag name from the method call.
So check the following:
irb(main):007:0> Nokogiri::XML::Builder.new { |xml| xml.send_ "foo" }.to_xml
=> "<?xml version=\"1.0\"?>\n<send>foo</send>\n"

How to navigate a XML object in Ruby

I have a regular xml object created from a response of a web service.
I need to get some specific values from some specific keys... for example:
<tag>
<tag2>
<tag3>
<needThisValue>3</needThisValue>
<tag4>
<needThisValue2>some text</needThisValue2>
</tag4>
</tag3>
</tag2>
</tag>
How can I get <needThisValue> and <needThisValue2> in Ruby?
I'm a big fan of Nokogiri:
xml = <<EOT
<tag>
<tag2>
<tag3>
<needThisValue>3</needThisValue>
<tag4>
<needThisValue2>some text</needThisValue2>
</tag4>
</tag3>
</tag2>
</tag>
EOT
This creates a document for parsing:
require 'nokogiri'
doc = Nokogiri::XML(xml)
Use at to find the first node matching the accessor:
doc.at('needThisValue2').class # => Nokogiri::XML::Element
Or search to find all nodes matching the accessor as a NodeSet, which acts like an Array:
doc.search('needThisValue2').class # => Nokogiri::XML::NodeSet
doc.search('needThisValue2')[0].class # => Nokogiri::XML::Element
This uses a CSS accessor to locate the first instance of each node:
doc.at('needThisValue').text # => "3"
doc.at('needThisValue2').text # => "some text"
Again with the NodeSet using CSS:
doc.search('needThisValue')[0].text # => "3"
doc.search('needThisValue2')[0].text # => "some text"
You can use XPath accessors instead of CSS if you want:
doc.at('//needThisValue').text # => "3"
doc.search('//needThisValue2').first.text # => "some text"
Go through the tutorials to get a jumpstart. It's very powerful and quite easy to use.
require "rexml/document"
include REXML
doc = Document.new string
puts XPath.first(doc, "//tag/tag2/tag3/needThisValue").text
puts XPath.first(doc, "//tag/tag2/tag3/tag4/needThisValue2").text
Try this Nokogiri tutorial.
You'll need to install nokogiri gem.
Good luck.
Check out the Nokogiri gem. You can read some tutorials enter link description here. It's fast and simple.

Is there a Nokogiri example code for parsing Acrobat XFDF?

I am looking for a ruby code snippet that shows use of Nokogiri to parse Acrobat XFDF data.
It's no different than parsing any other XML:
require 'nokogiri'
xfdf = '<?xml version="1.0" encoding="UTF-8"?>
<xfdf xmlns="http://ns.adobe.com/xfdf/" xml:space="preserve">
  <f href="Demo PDF Form.pdf"/>
  <fields>
    <field name="Date of Birth">
      <value>01-01-1960</value>
    </field>
    <field name="Your Name">
      <value>Mr. Customer</value>
    </field>
  </fields>
  <ids original="FEBDB19E0CD32274C16CE13DCF244AD2" modified="5BE74DD4F607B7409DC03D600E466E12"/>
</xfdf>
'
doc = Nokogiri::XML(xfdf)
doc.at('//xmlns:f')['href'] # => "Demo PDF Form.pdf"
doc.at('//xmlns:field[#name="Date of Birth"]').text # => "\n      01-01-1960\n    "
doc.at('//xmlns:field[#name="Your Name"]').text # => "\n      Mr. Customer\n    "
It uses a XML namespace, so you have to honor that in the xpaths, or deal with it by telling Nokogiri to ignore them, but this is common in XML.
You can use [nguyen][1] gem to do parsing job
xfdf = Nguyen::Xfdf.new(:key => 'value', :other_key => 'other value')
# use to_xfdf if you just want the XFDF data, without writing it to a file puts
xfdf.to_xfdf
# write xfdf file
xfdf.save_to('path/to/file.xfdf')

How to access attributes using Nokogiri

I have a simple task of accessing the values of some attributes. This is a simple script that uses Nokogiri::XML::Builder to create a simple XML doc.
require 'nokogiri'
builder = Nokogiri::XML::Builder.new(:encoding => 'UTF-8') do |xml|
xml.Placement(:messageId => "392847-039820-938777", :system => "MOD", :version => "2.0") {
xml.objects {
xml.object(:myattribute => "99", :anotherattrib => "333")
xml.nextobject_ '9387toot'
xml.Entertainment "Last Man Standing"
}
}
end
puts builder.to_xml
puts builder.root.attributes["messageId"]
The results are:
<?xml version="1.0" encoding="UTF-8"?>
<Placement messageId="392847-039820-938777" version="2.0" system="MOD">
<objects>
<object anotherattrib="333" myattribute="99"/>
<nextobject>9387toot</nextobject>
<Entertainment>Last Man Standing</Entertainment>
</objects>
</Placement>
C:/Ruby/lib/ruby/gems/1.8/gems/nokogiri-1.4.2-x86-mingw32/lib/nokogiri/xml/document.rb:178:in `add_child': Document already has a root node (RuntimeError)
from C:/Ruby/lib/ruby/gems/1.8/gems/nokogiri-1.4.2-x86-mingw32/lib/nokogiri/xml/node.rb:455:in `parent='
from C:/Ruby/lib/ruby/gems/1.8/gems/nokogiri-1.4.2-x86-mingw32/lib/nokogiri/xml/builder.rb:358:in `insert'
from C:/Ruby/lib/ruby/gems/1.8/gems/nokogiri-1.4.2-x86-mingw32/lib/nokogiri/xml/builder.rb:350:in `method_missing'
from C:/Documents and Settings/etrojan/workspace/Lads/tryXPATH2.rb:15
The XML that is generated looks fine. However, my attempts to access attributes cause an error to be generated:
Document already has a root node
I don't understand why puts would cause this error.
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
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")

Resources