Search code examples
c#xmlsubdirectory

C# - Directory Structure from XML


Using C# I have an XML file containing a folders structure, the user selects the main project folder then the system begins checking the sub-folders in the main project folder based on the structure of the XML file. The folder structure is dynamic as the admin of the application can add/remove/modify a folder within the structure by changing the XML file.

How do I loop through the XML, I tried using XmlDocument to get the full directory pathbut I read about using XDocument for better result but my knowledge of XML is still basic.

The XML file structure is:

<?xml version="1.0" encoding="utf-8" ?>

<dir name="Site Documents">
  <dir name="External">
    <dir name="Mechanical">
      <dir name="01. Submittals">
        <dir name="1. Sent">
        </dir>
        <dir name="2. Received" />
      </dir>
      <dir name="02. Drawings">
        <dir name="1. Sent">
        </dir>
        <dir name="2. Received" />
      </dir>
      <dir name="03. MIR">
        <dir name="1. Sent">
        </dir>
        <dir name="2. Received" />
      </dir>
      <dir name="04. IR">
        <dir name="1. Sent">
        </dir>
        <dir name="2. Received" />
      </dir>
    </dir>
    <dir name="Electrical">
      <dir name="01. Submittals">
        <dir name="1. Sent">
        </dir>
        <dir name="2. Received" />
      </dir>
      <dir name="02. Drawings">
        <dir name="1. Sent">
        </dir>
        <dir name="2. Received" />
      </dir>
      <dir name="03. MIR">
        <dir name="1. Sent">
        </dir>
        <dir name="2. Received" />
      </dir>
      <dir name="04. IR">
        <dir name="1. Sent">
        </dir>
        <dir name="2. Received" />
      </dir>
    </dir>
  </dir>
  <dir name="Internal">
    <dir name="01. PR">
      <dir name="1. MECH">
      </dir>
      <dir name="2. ELEC" />
    </dir>
    <dir name="02. PO">
    </dir>
    <dir name="03. SRF">
    </dir>
    <dir name="04. RMR" />
  </dir>
</dir>

EDIT -- 1

I tried using this test code to get the path of the node and sub-node

private void button2_Click(object sender, EventArgs e)
        {
            XmlDocument xDoc = new XmlDocument();
            xDoc.Load(@"C:\Users\John_Doe\_data\directory_hirarchy.xml");
            XmlNodeList xmlFolderName = xDoc.SelectNodes("//dir");
            MessageBox.Show(xmlFolderName.Count.ToString());
            string finalText = "";
            for (int ctr = 0; ctr < xmlFolderName.Count; ctr++)
            {
                string DocFolder = xmlFolderName[ctr].Attributes["name"].InnerText;
                finalText = finalText + DocFolder + "\r\n";
            }
            txtDisplay.Text = finalText; // Test text box for Output Result
        }

The output of the text box is:

Site Documents External Mechanical 01. Submittals 1. Sent 2. Received 02. Drawings 1. Sent 2. Received 03. MIR 1. Sent 2. Received 04. IR 1. Sent 2. Received Electrical 01. Submittals 1. Sent 2. Received 02. Drawings 1. Sent 2. Received 03. MIR 1. Sent 2. Received 04. IR 1. Sent 2. Received Internal 01. PR 1. MECH 2. ELEC 02. PO 03. SRF 04. RMR

Solved by Daisy Shipton

using System;
using System.Linq;
using System.Xml.Linq;

class Program
{
    static void Main()
    {
        var doc = XDocument.Load("test.xml");
        var directories = doc.Descendants("dir");

        foreach (var dir in directories)
        {
            var parts = dir
                .AncestorsAndSelf() // All the ancestors of this element, and itself
                .Reverse()          // Reversed (so back into document order)
                .Select(e => e.Attribute("name").Value); // Select the name
            var path = string.Join("/", parts);
            Console.WriteLine(path);
        }
    }   
}

Solution

  • There are two options here:

    • Use recursion, so you can keep track of "the path so far"
    • Just look at all elements, but use their ancestors to build up the path

    The first is probably simplest to understand, and yes, using LINQ to XML makes life simpler.

    using System;
    using System.Xml.Linq;
    
    class Program
    {
        static void Main()
        {
            var doc = XDocument.Load("test.xml");
            PrintDirectories(doc, null);        
        }
    
        static void PrintDirectories(XContainer parent, string path)
        {
            foreach (XElement element in parent.Elements("dir"))
            {
                string dir = element.Attribute("name").Value;
                string fullPath = path == null ? dir : $"{path}/{dir}";
                Console.WriteLine(fullPath);
                PrintDirectories(element, fullPath);
            }
        }
    }
    

    The non-recursive approach is about the same size, but possibly harder to understand if you're not familiar with LINQ:

    using System;
    using System.Linq;
    using System.Xml.Linq;
    
    class Program
    {
        static void Main()
        {
            var doc = XDocument.Load("test.xml");
            var directories = doc.Descendants("dir");
    
            foreach (var dir in directories)
            {
                var parts = dir
                    .AncestorsAndSelf() // All the ancestors of this element, and itself
                    .Reverse()          // Reversed (so back into document order)
                    .Select(e => e.Attribute("name").Value); // Select the name
                var path = string.Join("/", parts);
                Console.WriteLine(path);
            }
        }   
    }