This is very similar to this question . Here's a modified version based on sris answer :
paths = [ "nodeA1", "nodeA1/nodeB1/nodeC1", "nodeA1/nodeB1/nodeC1/nodeD1/nodeE1", "nodeA1/nodeB1/nodeC2", "nodeA1/nodeB2/nodeC2", "nodeA3/nodeB2/nodeC3" ] tree = {} paths.each do |path| current = tree path.split("/").inject("") do |sub_path,dir| sub_path = File.join(sub_path, dir) current[sub_path] ||= {} current = current[sub_path] sub_path end end def make_tree(prefix, node) tree = "" node.each_pair do |path, subtree| tree += "#{prefix}<#{File.basename(path)}" if subtree.empty? tree += "/>\n" else tree += ">\n" tree += make_tree(prefix + "\t", subtree) unless subtree.empty? tree += "#{prefix}</#{File.basename(path)}>\n" end end tree end xml = make_tree "", tree print xml
Edit:
Here is a modified version that creates the actual XML document using Nokogiri. I think this is actually easier than in the string version. I also removed the use of File , because you really don't need this to meet your needs:
require 'nokogiri' paths = [ "nodeA1", "nodeA1/nodeB1/nodeC1", "nodeA1/nodeB1/nodeC1/nodeD1/nodeE1", "nodeA1/nodeB1/nodeC2", "nodeA1/nodeB2/nodeC2", "nodeA3/nodeB2/nodeC3" ] tree = {} paths.each do |path| current = tree path.split("/").each do |name| current[name] ||= {} current = current[name] end end def make_tree(node, curr = nil, doc = Nokogiri::XML::Document.new)
Edit 2:
Yes, itβs true that in Ruby 1.8 the hash is not guaranteed to preserve insertion order. If this is a problem, there are ways around it. Here's a solution that keeps order, but doesn't worry about recursion and is much simpler for it:
require 'nokogiri' paths = [ "nodeA1", "nodeA1/nodeB1/nodeC1", "nodeA1/nodeB1/nodeC1/nodeD1/nodeE1", "nodeA1/nodeB1/nodeC2", "nodeA1/nodeB2/nodeC2", "nodeA3/nodeB2/nodeC3" ] doc = Nokogiri::XML::Document.new doc.root = Nokogiri::XML::Node.new('root', doc) paths.each do |path| curr = doc.root path.split("/").each do |name| curr = curr.xpath(name).first || curr << Nokogiri::XML::Node.new(name, doc) end end print doc