Search code examples
c#.netxmllinqxmldocument

Convert flat XML to hierarchical structure using LINQ


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.


Solution

  • 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);
                }
    
            }
        }
    
    }