This is very similar to this question. Here's a modified version based upon sris's 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 builds an actual XML document using Nokogiri. I think it's actually easier to follow than the string version. I also removed the use of File
, because you don't actually need it 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)
#You need a root node for the XML. Feel free to rename it.
curr ||= doc.root = Nokogiri::XML::Node.new('root', doc)
node.each_pair do |name, subtree|
child = curr << Nokogiri::XML::Node.new(name, doc)
make_tree(subtree, child, doc) unless subtree.empty?
end
doc
end
xml = make_tree tree
print xml
Edit 2:
Yes, it is true that in Ruby 1.8 hashes aren't guaranteed to maintain insertion order. If that's an issue, there are ways to work around it. Here's a solution that retains order but doesn't bother with 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