October 24, 2004

Indenting attributes with XmlTextWriter

XmlTextWriter in .NET 1.X only supports indentation of the following node types: DocumentType, Element, Comment, ProcessingInstruction, and CDATA. No attributes. So how to get attributes indented anyway? If you can - wait .NET 2.0 with cool XmlWriterSettings.NewLineOnAttributes, otherwise - here is a hack how to get attributes indented with XmlTextWriter ...

Well, XmlWriter isn't particularly low-level writer, it's abstract XML oriented API, so its implementation XmlTextWriter wouldn't allow you to just override WriteStartAttribute() method and inject indentation characters before each attribute - it would be considered as an exceptional attempt to write an attribute after a content has been already written. But when instantiating XmlTextWriter on top of some TextWriter, one can inject indentation before each attribute to that underlying TextWriter. It doesn't look particularly clean, but anyway:

public class AttributeIndentingXmlTextWriter : XmlTextWriter 
{
    private TextWriter w;
    private int depth;

    //Add constructors as needed
    public AttributeIndentingXmlTextWriter(TextWriter w)
        : base(w) 
    {
        this.w = w;
    }

    public override void WriteStartElement(string prefix, 
        string localName, string ns)    
    {
        depth ++;
        base.WriteStartElement(prefix, localName, ns);
    }

    public override void WriteFullEndElement()
    {
        depth--;
        base.WriteFullEndElement();
    }    

    public override void WriteEndElement()
    {
        depth--;
        base.WriteEndElement();
    }    

    public override void WriteStartAttribute(string prefix, 
        string localName, string ns)
    {
        if (base.Formatting == Formatting.Indented) 
        {   
            w.WriteLine();
            for (int i=1; i<Indentation*depth; i++)
                w.Write(IndentChar);
        }
        base.WriteStartAttribute(prefix, localName, ns);
    }
}
Usage:
XmlTextWriter w = 
  new AttributeIndentingXmlTextWriter(Console.Out);
w.Formatting = Formatting.Indented;
w.WriteStartDocument();
w.WriteStartElement("foo");
w.WriteAttributeString("attr1", "value1");
w.WriteAttributeString("attr2", "value2");
w.WriteAttributeString("attr3", "value3");
w.WriteStartElement("bar");
w.WriteAttributeString("attr1", "value1");
w.WriteAttributeString("attr2", "value2");
w.WriteAttributeString("attr3", "value3");
w.WriteString("some text");
w.WriteEndElement();
w.WriteEndElement();
w.WriteEndDocument();
The result is as follows:
<foo
  attr1="value1"
  attr2="value2"
  attr3="value3">
  <bar
    attr1="value1"
    attr2="value2"
    attr3="value3">some text</bar>
</foo>

Samples are templates

DonXML writes on viral coding examples in presentations on using XML in .NET: Joe Fawcett (fellow XML MVP) came across a great example (from the Microsoft.Public.Xml newsgroup) of one of my biggest pet peeves, "We (the community) are doing a very poor job teaching the average developer how to use ...