When developing custom XmlReader or XmlWriter in .NET 2.0 there is at least three options:
First way is a for full-blown XmlReader/XmlWriters, which need to implement each aspects of XML reading/writing in a different way, e.g. XmlCsvReader, which reads CSV as XML or XslReader, which streamlines XSLT output. This is the most clean while the hardest way - XmlReader has 26 (and XmlWriter - 24) abstract members you would have to implement. Second and third options are for override-style custom readers/writers. When you only need to partially modify XmlReader/XmlWriter behavior it's an overkill to reimplement it from scratch. As this is the most usual scenario I'll concentrate on these two options.
OOP taught us - inherit the class whose behaviour you want to modify and override its virtual members. That's the easiest and most popular way of writing custom XmlReader/XmlWriter. For example let's say you are receiving XML documents from your business partners and one particularly annoying one keeps sending element <value>, while according to the schema you expect it to be <price>. So you need renaming plumbing - XmlReader that reads <value> as <price> and XmlWriter that writes <price> as <value>. Here are implementations extending standard XmlTextReader and XmlTextWriter classes:
public class RenamingXmlReader : XmlTextReader
{
//Provide as many constructors as you need
public RenamingXmlReader(string file)
: base(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 : XmlTextWriter
{
//Provide as many constructors as you need
public RenamingXmlWriter(TextWriter output)
: base(output) { }
public override void WriteStartElement(string prefix,
string localName, string ns)
{
if (string.IsNullOrEmpty(prefix) && localName == "price")
{
localName = "value";
}
base.WriteStartElement(prefix, localName, ns);
}
}
Looks nice, but there is a couple of serious drawbacks in this approach though:
So beware that when deriving from XmlTextReader/XmlTextWriter you are gonna inherit all their crappy legacy weirdnesses too.
A third approach is to wrap XmlReader/XmlWriter created via Create() method and override methods you need. This approach is used by .NET itself. This requires a little bit more code, but as a result you get clean design and easily composable implementation. I'll cover it tomorrow 'cause Tel-Aviv traffic jams are waiting for me now.