patternrubyMinor
Building XML by enumerating through array & hashes
Viewed 0 times
arrayenumeratingbuildingxmlhashesthrough
Problem
data =
[ { 'name' => 'category1',
'subCategory' => [ {'name' => 'subCategory1',
'product' => [ {'name' => 'prodcutName1',
'desc' => 'desc1'},
{'name' => 'prodcutName2',
'desc' => 'desc2'}]
} ]
},
{
#category2 ...and so on
}
]Just recently finished a small project with Ruby. I used the above array of hashes to produce a XML.
I think my solution is quite messy nesting loops and builder tags. Can anyone help me out with a more elegant approach?
builder = Nokogiri::XML::Builder.new { |xml|
data.each { |category| # go through every category in the data array
xml.category {
xml.name category['name']
category['subCategory'].each { |subCategory| # go through each subCategory in the category
xml.subCategory {
xml.name subCategory['name']
subCategory['product'].each { |product| # go though each products and print their data
xml.name product['name']
xml.desc product['desc']
}
}
}
}
}
puts builder.to_xmlSolution
Here's a nice recursive solution, that creates
When used with this data…
…you get this result:
However, if I had control over the XML schema, I'd do this instead:
…but perhaps you're dealing with some terrible XML schema like PList.
value from 'key'=>'value' entries in your hash. If the value is an array, it instead recurses, using the key name as a wrapper element.require 'nokogiri'
def process_array(label,array,xml)
array.each do |hash|
xml.send(label) do # Create an element named for the label
hash.each do |key,value|
if value.is_a?(Array)
process_array(key,value,xml) # Recurse
else
xml.send(key,value) # Create value (using variables)
end
end
end
end
end
builder = Nokogiri::XML::Builder.new do |xml|
xml.root do # Wrap everything in one element.
process_array('category',data,xml) # Start the recursion with a custom name.
end
end
puts builder.to_xml
When used with this data…
data = [
{ 'name' => 'category1',
'subCategory' => [
{ 'name' => 'subCategory1',
'product' => [
{ 'name' => 'productName1',
'desc' => 'desc1' },
{ 'name' => 'productName2',
'desc' => 'desc2' } ]
} ]
},
{ 'name' => 'category2',
'subCategory' => [
{ 'name' => 'subCategory2.1',
'product' => [
{ 'name' => 'productName2.1.1',
'desc' => 'desc1' },
{ 'name' => 'productName2.1.2',
'desc' => 'desc2' } ]
} ]
},
]
…you get this result:
category1
subCategory1
productName1
desc1
productName2
desc2
category2
subCategory2.1
productName2.1.1
desc1
productName2.1.2
desc2
However, if I had control over the XML schema, I'd do this instead:
require 'nokogiri'
def process_array(label,array,xml)
array.each do |hash|
kids,attrs = hash.partition{ |k,v| v.is_a?(Array) }
xml.send(label,Hash[attrs]) do
kids.each{ |k,v| process_array(k,v,xml) }
end
end
end
builder = Nokogiri::XML::Builder.new do |xml|
xml.root{ process_array('category',data,xml) }
end
puts builder.to_xml
…but perhaps you're dealing with some terrible XML schema like PList.
Code Snippets
require 'nokogiri'
def process_array(label,array,xml)
array.each do |hash|
kids,attrs = hash.partition{ |k,v| v.is_a?(Array) }
xml.send(label,Hash[attrs]) do
kids.each{ |k,v| process_array(k,v,xml) }
end
end
end
builder = Nokogiri::XML::Builder.new do |xml|
xml.root{ process_array('category',data,xml) }
end
puts builder.to_xml<?xml version="1.0"?>
<root>
<category name="category1">
<subCategory name="subCategory1">
<product name="productName1" desc="desc1"/>
<product name="productName2" desc="desc2"/>
</subCategory>
</category>
<category name="category2">
<subCategory name="subCategory2.1">
<product name="productName2.1.1" desc="desc1"/>
<product name="productName2.1.2" desc="desc2"/>
</subCategory>
</category>
</root>Context
StackExchange Code Review Q#51569, answer score: 8
Revisions (0)
No revisions yet.