Jun 10 2008

I wanna make my XML pretty...

Category: Tips and TricksJoeGeeky @ 13:51

Although not appropriate for most end-users there are a lot of situations where you need to render raw XML in your presentation layer.  One problem that often arises when doing this is dealing the the structure of the XML.  Some XML may come preformatted with identations, line breaks, and carriage returns while others may not.  In either case you will want to structure the XML in a consistent manner. This is certainly not a unique problem and if you Google things like "Pretty Print XML" you will find a lot of examples in a variety of languages on how to do this. However the .NET examples often leave out one important detail that can lead to exceptions...  Lets take a look...

Here is a common C# pattern you will find online:

/// <summary>
/// Apply Xml formatting such as 
/// indentation and line feeds
/// </summary>
public static string FormatXml(string xml)
{
    string returnValue;    
    try
    {
        XmlDocument xmlDoc = new XmlDocument();
        xmlDoc.LoadXml(xml);
        XmlNodeReader xReader = new XmlNodeReader(xmlDoc);
        StringWriter sWriter;
        sWriter = new StringWriter();
        XmlTextWriter xWriter = new XmlTextWriter(sWriter);
        xWriter.Formatting=Formatting.Indented;
        xWriter.WriteNode(xReader, true);
        xWriter.Close();
        returnValue = sWriter.ToString();
    }
    catch (XmlException)
    {
        returnValue = String.Empty;
    }
    return returnValue;
}

The above example will work fine when dealing with XML fragments or XML generated from object serialization routines, but will throw exceptions when it receives XML that is bound to DTDs or XSD Schemas. The problem often occures when the layers/components that work with the XML have access to the actual DTD/XSD files and the later that renders that XML for presentation do not... The solution to this problem is pretty easy but may not be completely obvious to people new to the objects involved.

Here is the same routine modified to address this situation:

/// <summary>
/// Apply Xml formatting such as 
/// indentation and line feeds
/// </summary>
public static string FormatXml(string xml)
{
    string returnValue;
    XmlDocument xmlDoc = new XmlDocument();
    //Configure the reader to skip all 
    //forms of validation
    XmlReaderSettings settings = new XmlReaderSettings();
    //Disable Schema Validation
    settings.ValidationType = ValidationType.None;
    settings.ValidationFlags = XmlSchemaValidationFlags.None;
    settings.ProhibitDtd = false;
    //Do not attempt to download DTD/XSD
    settings.XmlResolver = null;
    StringReader sReader = new StringReader(xml);
    XmlReader xReader = XmlReader.Create(sReader, settings);
    try
    {
        xmlDoc.Load(xReader);
        XmlNodeReader xmlReader = new XmlNodeReader(xmlDoc);
        StringWriter stringWriter = new StringWriter();
        XmlTextWriter xmlWriter = new XmlTextWriter(stringWriter);
        xmlWriter.Formatting = Formatting.Indented;
        xmlWriter.WriteNode(xmlReader, true);
        xmlWriter.Close();
        returnValue = stringWriter.ToString();
    }
    catch (XmlException)
    {
        returnValue = String.Empty;
    }
    return returnValue;
}

With that behind you can now consider other aspects of your transformation routines.  As always you want to make sure your code gives you the flexibility meet a variety of usage patterns.  Here is an example of some overloads you might find useful...

Tags: