Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to create xml element with attribute and value in Nokogiri

I'm using Rails 2.1.0 and Nokogiri 1.6.1. What I want seems pretty simple. I want my Rails Rest API to return XML with an element like this:

<PeopleNumber unit="NumberOfPeople">2.235075</PeopleNumber>

I tried writing something like:

xml = Nokogiri::NML::Builder.new do |xml|
  xml.PeopleNumber(:unit => "NumberOfPeople") 2.235075

ActionController fires off a syntax error.

If I try re-writing this as

xml = Nokogiri::NML::Builder.new do |xml|
  xml.PeopleNumber(:unit => "NumberOfPeople") { 2.235075 }

I get something like

<PeopleNumber unit="NumberOfPeople" />

Does anyone know of a way to get the desired behavior in Nokogiri?

like image 953
user1327955 Avatar asked Oct 31 '22 16:10

user1327955


2 Answers

I know this is quite old question, but even I stumbled upon same problem.
I found a clean solution.

builder = Nokogiri::NML::Builder.new do |xml|
  xml.PeopleNumber(2.235075, :unit => "NumberOfPeople")
end

now if you debug the XML output you will get the right output

puts builder.to_xml
# output will be following
<PeopleNumber unit="NumberOfPeople">2.235075</PeopleNumber>

Explanation: When you want to set some attributes and text content to XML tag using Nokogiri builder then you need to directly pass text content as first argument and then other attributes as key-value pairs same as we pass to any method.

builder = Nokogiri::NML::Builder.new do |xml|
  xml.YourTagName(PLAIN_TEXT_CONTENT, attr1: value1, attr2: value2, attrN: valueN)
end

This will output as

puts builder.to_xml
# output will be following
<YourTagName attr1="value1" attr2="value2" attrN="valueN">PLAIN_TEXT_CONTENT</YourTagName>

Even there are other ways but this is the cleanest one.

like image 165
Anand Bait Avatar answered Nov 15 '22 07:11

Anand Bait


Do it the simple way:

require 'nokogiri'

doc = Nokogiri::XML('<foo></foo>')
doc.at('foo').add_child('<PeopleNumber unit="NumberOfPeople">2.235075</PeopleNumber>')
puts doc.to_xml
# >> <?xml version="1.0"?>
# >> <foo>
# >>   <PeopleNumber unit="NumberOfPeople">2.235075</PeopleNumber>
# >> </foo>

The trick is add_child, which can take a predefined node, or a string consisting of the XML you want to add. From the documentation:

Add node_or_tags as a child of this Node. node_or_tags can be a Nokogiri::XML::Node, a ::DocumentFragment, a ::NodeSet, or a string containing markup.

"a string containing markup" is a free-pass to doing it an easy way.

If you need a different value for the unit parameter, or a different value for the tag itself, you can interpolate those into the string:

foo = 'WheelSize'
bar = '355/113'

doc = Nokogiri::XML('<foo></foo>')
doc.at('foo').add_child("<PeopleNumber unit='#{foo}'>#{bar}</PeopleNumber>")
puts doc.to_xml
# >> <?xml version="1.0"?>
# >> <foo>
# >>   <PeopleNumber unit="WheelSize">355/113</PeopleNumber>
# >> </foo>

Or you can directly modify the DOM and nodes:

doc = Nokogiri::XML('<foo><PeopleNumber /></foo>')
people_number = doc.at('PeopleNumber')
people_number['unit'] = 'fred'
people_number.content = 'ethel'
puts doc.to_xml
# >> <?xml version="1.0"?>
# >> <foo>
# >>   <PeopleNumber unit="fred">ethel</PeopleNumber>
# >> </foo>

There are other ways to do this in addition, but it's really up to you to use whatever fits your head best.

like image 35
the Tin Man Avatar answered Nov 15 '22 05:11

the Tin Man