Reputation: 4125
I have an XML document in a flat format:
<root>
<node-one>
<parent-id />
<node-id>1</node-id>
<value>foo</value>
</node-one>
<node-two>
<parent-id>1</parent-id>
<node-id>2</node-id>
<value>bar</value>
</node-two>
<node-three>
<parent-id>1</parent-id>
<node-id>3</node-id>
<value>baz</value>
</node-three>
<node-four>
<parent-id>3</parent-id>
<node-id>4</node-id>
<value>qux</value>
</node-four>
</root>
I want to convert it to hierarchical tree-like structure like this:
<root>
<node-one>
<parent-id />
<node-id>1</node-id>
<value>foo</value>
<node-two>
<parent-id>1</parent-id>
<node-id>2</node-id>
<value>bar</value>
</node-two>
<node-three>
<parent-id>1</parent-id>
<node-id>3</node-id>
<value>baz</value>
<node-four>
<parent-id>3</parent-id>
<node-id>4</node-id>
<value>qux</value>
</node-four>
</node-three>
</node-one>
</root>
Is there an elegant way to achieve it using XmlDocument/XDocument
?
Any help would greatly appreciated.
Upvotes: 0
Views: 440
Reputation: 34419
Try a recursive algorithm
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Xml;
using System.Xml.Linq;
namespace ConsoleApplication4
{
class Program
{
const string FILENAME = @"c:\temp\test.xml";
static List<XElement> nodes;
static void Main(string[] args)
{
XDocument doc = XDocument.Load(FILENAME);
nodes = doc.Root.Elements().ToList();
XElement parent = new XElement("root");
RecursvieAdd(parent, "");
XDocument doc2 = new XDocument(parent);
}
static void RecursvieAdd(XElement parent, string parentId)
{
foreach(XElement child in nodes.Where(x => (string)x.Element("parent-id") == parentId))
{
XElement newChild = new XElement(child);
parent.Add(newChild);
string id = (string)child.Element("node-id");
RecursvieAdd(newChild, id);
}
}
}
}
Upvotes: 1
Reputation: 148
You can either use XPaths or Linq to get this done. Since I used XPath recently I am going to show you how to go this way.
(You might have to add some references to System.Xml in the reference browser.)
Check this sample code (including comments for hints):
using System;
using System.Linq;
using System.Xml.Linq;
using System.Xml.XPath;
namespace xmlTest
{
internal class Program
{
public static void Main(string[] args)
{
var document = CreateDocument();
// Gets all children of the root element whose name starts with "node".
var nodeElements = document.XPathSelectElements("/root/*[starts-with(name(), 'node')]").ToList();
// Creates Tuples in the fashion: { 1, $NODE_ONE }, { 2, $NODE_TWO }, ...
// This is done because some values might be skipped.
var indexedNodes = nodeElements.Select(x => new Tuple<int, XElement>(int.Parse(x.Descendants("node-id").First().Value), x)).ToList();
foreach(var indexedNode in indexedNodes)
{
var parentId = GetParentNodeId(indexedNode.Item2);
if (parentId != null)
{
// Remove the node from its parent.
indexedNode.Item2.Remove();
// Add the node to the new parent.
var newParent = indexedNodes.First(x => x.Item1 == parentId).Item2;
newParent.Add(indexedNode.Item2);
}
}
Console.WriteLine(document.ToString());
}
static int? GetParentNodeId(XElement element) {
try {
var parentId = int.Parse(element.Descendants("parent-id").First().Value);
return parentId;
}
catch // Add some appropriate error handling here.
{
return null;
}
}
private static XDocument CreateDocument()
{
const string xml =
"<root> <node-one> <parent-id /> <node-id>1</node-id> <value>foo</value> </node-one> <node-two> <parent-id>1</parent-id> <node-id>2</node-id> <value>bar</value> </node-two> <node-three> <parent-id>1</parent-id> <node-id>3</node-id> <value>baz</value> </node-three> <node-four> <parent-id>3</parent-id> <node-id>4</node-id> <value>qux</value> </node-four> </root>";
return XDocument.Parse(xml);
}
}
}
As you can see from the output all the changes you have done to the elements are already reflected in the XDocument. That's because when working with the X-classes all changed are made in place.
Edit: You will need to change some code to include this into your application, e.g. the CreateDocument
method but that should be easy enough :)
Upvotes: 0