.NET: Recursively Format XElement for Readability

The .NET Framework’s XElement class is a nifty way to construct XML documents on the fly. Here is an example:

XElement request = new XElement(
    "Request",
    new XElement(
        "Type",
        new XAttribute("Value", "Order")),
    new XElement(
        "Customer",
        new XAttribute("Name", "Chris"),
        new XAttribute("State", "Arizona")),
    new XElement(
        "Password",
        new XAttribute("Value", string.Empty)),
    new XElement(
        "Repository",
        new XAttribute("CreateRepository", "False"),
        new XAttribute("JobID", "1234"),
        new XAttribute("CustomerFilesDirectory", @"\\SABC\Requests\2987194\")));

Which generates the following XML:

<Request>
    <Type Value="Order" /> 
    <Customer Name="Chris" State="Arizona" /> 
    <Password Value="" /> 
    <Repository CreateRepository="False" JobID="1234" CustomerFilesDirectory="\\SABC\Requests\2987194\" />
</Request>

And this is all very nice. Unless you have an element with, say, dozens of attributes. Then a given line can be very long and require excessive horizontal scrolling when reviewing the XML.

The XElement class doesn’t support any custom formatting, so I’ve developed a script which turns the above into the following:

<Request>
    <Type
        Value="Order" />
    <Customer
        Name="Chris"
        State="Arizona" />
    <Password
        Value="" />
    <Repository
        CreateRepository="False"
        JobID="1234"
        CustomerFilesDirectory="\\SABC\Requests\2987194\" />
</Request>

Here is the method, which calls itself via recursion, and a helper method to track indentation:

private static void FormatXElement(XElement xElement, ref string s, int indentLevel = 0)
{
    s += string.Format("{0}<{1}", GetIndent(indentLevel), xElement.Name.LocalName);

    var attributes = xElement.Attributes().ToList();

    foreach (var attribute in attributes)
    {
        indentLevel++;
        s += Environment.NewLine;
        s += string.Format("{0}{1}", GetIndent(indentLevel), attribute);
        indentLevel--;
    }

    var descendantNodes = xElement.DescendantNodes().ToList();

    if (descendantNodes.Any())
    {
        s += string.Format(">{0}", Environment.NewLine);

        foreach (var descendantNode in descendantNodes)
        {
            indentLevel++;
            FormatXElement((XElement)descendantNode, ref s, indentLevel);
            indentLevel--;
        }

        s += string.Format("</{0}>{1}", xElement.Name.LocalName, Environment.NewLine);
    }
    else
    {
        s += string.Format(" />{0}", Environment.NewLine);
    }
}

private static string GetIndent(int indentLevel)
{
    var indent = string.Empty;

    for (var x = 0; x < indentLevel; x++)
    {
        indent += "    ";
    }

    return indent;
}

That’s it! I’ve posted source code of a fully working demo on .NET Fiddle.

Tagged on: , ,

Leave a Reply

Your email address will not be published. Required fields are marked *