views:

2913

answers:

5

When I build XML up from scratch with XmlDocument, the OuterXml property already has everything nicely indented with line breaks. However, if I call LoadXml on some very "compressed" XML (no line breaks or indention) then the output of OuterXml stays that way. So ...

What is the simplest way to get beautified XML output from an instance of XmlDocument?

+4  A: 

As taken from Erika Ehrli's blog, this should do it:

XmlDocument doc = new XmlDocument();
doc.LoadXml("<item><name>wrench</name></item>");
// Save the document to a file and auto-indent the output.
XmlTextWriter writer = new XmlTextWriter("data.xml",null);
writer.Formatting = Formatting.Indented;
doc.Save(writer);
DocMax
+1  A: 
XmlTextWriter xw = new XmlTextWriter(writer);
xw.Formatting = Formatting.Indented;
benPearce
+5  A: 

Based on the other answers, I looked into XmlTextWriter and came up with the following helper method:

static public string Beautify(XmlDocument doc)
{
    StringBuilder sb = new StringBuilder();
    XmlWriterSettings settings = new XmlWriterSettings();
    settings.Indent = true;
    settings.IndentChars = "  ";
    settings.NewLineChars = "\r\n";
    settings.NewLineHandling = NewLineHandling.Replace;
    XmlWriter writer = XmlWriter.Create(sb, settings);
    doc.Save(writer);
    writer.Close();
    return sb.ToString();
}

It's a bit more code than I hoped for, but it works just peachy.

Neil C. Obremski
You might even consider creating your utility method as an extension method to the XmlDocument class.
Oppositional
A: 

If the above Beautify method is being called for an XmlDocument that already contains an XmlProcessingInstruction child node the following exception is thrown:

Cannot write XML declaration. WriteStartDocument method has already written it.

This is my modified version of the original one to get rid of the exception:

private static string beautify(
    XmlDocument doc)
{
    var sb = new StringBuilder();
    var settings =
        new XmlWriterSettings
            {
                Indent = true,
                IndentChars = @"    ",
                NewLineChars = Environment.NewLine,
                NewLineHandling = NewLineHandling.Replace,
            };

    using (var writer = XmlWriter.Create(sb, settings))
    {
        if (doc.ChildNodes[0] is XmlProcessingInstruction)
        {
            doc.RemoveChild(doc.ChildNodes[0]);
        }

        doc.Save(writer);
        return sb.ToString();
    }
}

It works for me now, probably you would need to scan all child nodes for the XmlProcessingInstruction node, not just the first one?

Uwe Keim
A: 

A shorter extension method version

    public static string ToIndentedString( this XmlDocument doc )
    {
        var stringWriter = new StringWriter(new StringBuilder());
        var xmlTextWriter = new XmlTextWriter(stringWriter) {Formatting = Formatting.Indented};
        doc.Save( xmlTextWriter );
        return stringWriter.ToString();
    }
Jonathan Mitchem