May 31, 2006

On creating custom XmlReaders/XmlWriters in .NET 2.0, Part 2

This is second part of the post. Find first part here.

So what is a better way of creating custom XmlReader/XmlWriter in .NET 2.0? Here is the idea - have an utility wrapper class, which wraps XmlReader/XmlWriter and does nothing else. Then derive from this class and override methods you are interested in. These utility wrappers are called XmlWrapingReader and XmlWrapingWriter. They are part of System.Xml namespace, but unfortunately they are internal ones - Microsoft XML team has considered making them public, but in the Whidbey release rush decided to postpone this issue. Ok, happily these classes being pure wrappers have no logic whatsoever so anybody who needs them can indeed create them in a 10 minutes. But to save you that 10 minutes I post these wrappers here. I will include XmlWrapingReader and XmlWrapingWriter into the next Mvp.Xml library release.

...

Btw, most likely the Mvp.Xml project will be rehosted to the CodePlex. SourceForge is ok, but the idea is that more Microsoft-friendly environment will induce more contributors both XML MVPs and others.

+ XmlWrappingReader class.

+ XmlWrappingWriter class.

Now instead of deriving from legacy XmlTextReader/XmlTextWriter you derive from XmlWrappingReader/XmlWrappingWriter passing to their constructors XmlReader/XmlWriter created via Create() factory method:
public class RenamingXmlReader : XmlWrappingReader
{
    //Provide as many contructors as you need
    public RenamingXmlReader(string file)
        : base(XmlReader.Create(file)) { }

    public override string Name
    {
        get
        {
            return (NodeType == XmlNodeType.Element && 
                base.Name == "value") ? 
                "price" : base.Name;
        }
    }

    public override string LocalName
    {
        get
        {
            return (NodeType == XmlNodeType.Element && 
                base.LocalName == "value") ?
                "price" : base.LocalName;
        }
    }
}
public class RenamingXmlWriter : XmlWrappingWriter
{
    //Provide as many contructors as you need
    public RenamingXmlWriter(TextWriter output)
        : base(XmlWriter.Create(output)) { }

    public override void WriteStartElement(string prefix, string localName, string ns)
    {
        if (string.IsNullOrEmpty(prefix) && localName == "price")
        {
            localName = "value";
        }
        base.WriteStartElement(prefix, localName, ns);
    }
}

That's it. Not much different from previous option in terms of coding, but free of weird XmlTextReader/XmlTextWriter legacy.

AFAIK there is still one problem with this approach though, which is DTD validation. I mean cascading validating XmlReader on top of custom XmlReader scenario. E.g. if you need to resolve XInclude or rename couple of elements and validate resulting XML against DTD in one shot. In .NET 2.0 if you want to DTD validate XML that comes from another XmlReader that reader must be an instance of XmlTextReader. That's undocumented limitation and it was left sort of deliberatly - after all who cares about DTD nowadays? XML Schema validation is not affected by this limitation.