Saturday, December 1, 2012

C# Reading XML Recursively (XmlDocument)

I'll be honest - I hate Recursion! But, nothing you can do, there are some situations when you know - this problem should be solved with Recursion. One of such problems is XML reading.
Today I'll show a simple example of reading XmlDocument recursively.

This is our XML:
<employees>
    <employee Id="1001" Name="John" Position="Developer">    
        <projects>
            <project Code="Rocket">                
                <budget>7000</budget>
            </project>
        </projects>
    </employee>

    <employee Id="1002" Name="Tomas" Position="Helpdesk">    
        <projects>
            <project Code="Orlando">                
                <budget>1000</budget>
            </project>
            <project Code="Newman">                
                <budget>900</budget>
            </project>
        </projects>
    </employee>
    
    <employee Id="1003" Name="Susan" Position="QA">        
        <projects>            
            <project Code="Newman">                
                <budget>900</budget>
            </project>
            <project Code="Rocket">                
                <budget>7000</budget>
            </project>
            <project Code="Orlando">                
                <budget>1000</budget>
            </project>
        </projects>
    </employee>  
</employees>
And, for example, I want to print the names of employees that participated in Orlando project. Of course, this task could be done with LINQ, but this time we want to see some recursion, right? :) Here is the code:
static void Main(string[] args)
{
    XmlDocument doc = new XmlDocument();
    doc.Load("../../Employees.xml");
    XmlNode root = doc.SelectSingleNode("*"); 
    ReadXML(root);
}

private static void ReadXML(XmlNode root)
{
    if (root is XmlElement)
    {
        DoWork(root);

        if (root.HasChildNodes)
            ReadXML(root.FirstChild);
        if (root.NextSibling != null)
            ReadXML(root.NextSibling);
    }
    else if (root is XmlText)
    {}
    else if (root is XmlComment)
    {}
}

private static void DoWork(XmlNode node)
{
    if (node.Attributes["Code"] != null)
        if(node.Name == "project" && node.Attributes["Code"].Value == "Orlando")
            Console.WriteLine(node.ParentNode.ParentNode.Attributes["Name"].Value);
}
Reading XML recursively in C# - result

So, I go over each XmlNode by calling XMLRead method. Of course, we need some logic, which is in DoWork method. Inside the XMLRead method I specified that I'm interesting only in XmlElement. As you can see from the source code, you can specify not only XmlElement, but also XmlComment or other XmlNode type, if you need. All this stuff looks very simple until you starting to remove XML elements. Don't forget that once you removed an XML node, the whole XML structure is changed.

Download the source code (Visual Studio 2012 project).

3 comments:

  1. Thank you very much for this great explain.

    ReplyDelete
  2. Thank you! I have to do this quite often, so I turned it into a method:


    ParseAllNodes(
    node: doc.DocumentElement, //Recursively parse the children of this node
    doXmlElement: n => { }, //Do nothing with XmlElement nodes
    doXmlText: n => {
    n.InnerText = string.Empty; //Remove all inner text from XmlText nodes
    },
    doXmlComment: n => { } //Do nothing with XmlComment nodes
    );


    private static void ParseAllNodes(XmlNode node, Action doXmlElement, Action doXmlText, Action doXmlComment)
    {
    if (node is XmlElement)
    {
    doXmlElement(node); //Do stuff to XmlElements

    if (node.HasChildNodes)
    {
    ParseAllNodes(node.FirstChild, doXmlElement, doXmlText, doXmlComment); //Recurse
    }
    if (node.NextSibling != null)
    {
    ParseAllNodes(node.NextSibling, doXmlElement, doXmlText, doXmlComment); //Recurse
    }
    }
    else if (node is XmlText) doXmlText(node); //Do stuff to XmlTexts
    else if (node is XmlComment) doXmlComment(node); //Do stuff to XmlComments
    }

    ReplyDelete
  3. this is great thanks.
    i'm trying to go through an XML, and only keep certain nodes, how can i add this to your solution, how to integrate the output XML element ?

    thanks,
    dave

    ReplyDelete