XML forking in .NET

Introduction

This article will show how to fork XML processing when using XmlReaders and XmlWriters in .NET Framework 1.1.

Background

Recently, in an application, I had the need to log all XML messages exchanged with service providers.

Being services implemented over the POX/REST(Plain Old XML/REpresentational State Transfer) protocol, I had thedisadvantage of not being able to take advantage of the SOAPimplementation in the .NET framework but, because I had to do itmyself, I would have some control over the messages.

Part of the message content belonged to theprotocol implemented by the service provider (something like SOAP), andonly a small part of it was service specific content. The serviceclients use DTOs (Data Transfer Objects) which were serialized usingXmlSerializers.

To make it more difficult to tamper withmessage content and to gain some performance, I opted by not usingXmlDocuments, instead, I used XmlReaders and XmlWriters.

Because XmlSerializers were being used, theneed to log message content posed an interesting problem, becauseXmlReaders and XmlWriters were, some times, out of my control and underthe control of XmlSerializers. Besides that, message logging could beoptional, therefore, I needed something that I could turn "on" or "off"whenever I needed.

Forking XML processing

The requirement looked simple; I only neededto pass a XmlWriter that would log every node being processed by theXmlReaders and XmlWriters used to process messages. And all this had tobe transparent to the rest of the application (and any library orframework used by the application).

In the presence of such a task, I didn'trefrain myself from getting help from the experts. Larry Serflatenpointed out that the concept I was looking for was message forking andOleg Tkachenko showed me the way to its implementation.

Forking XML reading

To fork the reading operation of XML messages,I opted on building a XmlReader derived class (XmlForkedReader) thatwould receive, on instantiation of its objects, an instance of aXmlReader derived class used to read the message and an instance of aXmlWriter derived class used to log the message.

I opted also to implement IDisposable to takeadvantage of the use of using (Using in Visual Basic) blocks and to becompliant with the implementation of the XmlReader in .NET framework2.0.

public class XmlForkedReader : System.Xml.XmlReader,
System.IDisposable
{
public XmlForkedReader(System.Xml.XmlReader reader,
System.Xml.XmlWriter forkedWriter) : base()
{

}
}

The whole trick lies in the Read method.Depending on the XmlReader's current node type, the correspondingoperation is performed on the wrapped XmlReader and the forkedXmlWriter used to log the message.

public override bool Read()
{
bool read = this.reader.Read();

switch (reader.NodeType)
{
case System.Xml.XmlNodeType.Element:
forkedWriter.WriteStartElement(reader.Prefix,
reader.LocalName, reader.NamespaceURI);
forkedWriter.WriteAttributes(reader,
this.writeDefaultAttributes);
if (reader.IsEmptyElement)
{
forkedWriter.WriteEndElement();
}
break;
case System.Xml.XmlNodeType.Text:
forkedWriter.WriteString(reader.Value);
break;
case System.Xml.XmlNodeType.Whitespace:
case System.Xml.XmlNodeType.SignificantWhitespace:
forkedWriter.WriteWhitespace(reader.Value);
break;
case System.Xml.XmlNodeType.CDATA:
forkedWriter.WriteCData(reader.Value);
break;
case System.Xml.XmlNodeType.EntityReference:
forkedWriter.WriteEntityRef(reader.Name);
break;
case System.Xml.XmlNodeType.XmlDeclaration:
case System.Xml.XmlNodeType.ProcessingInstruction:
forkedWriter.WriteProcessingInstruction(reader.Name,
reader.Value);
break;
case System.Xml.XmlNodeType.DocumentType:
forkedWriter.WriteDocType(reader.Name,
reader.GetAttribute("PUBLIC"),
reader.GetAttribute("SYSTEM"),
reader.Value);
break;
case System.Xml.XmlNodeType.Comment:
forkedWriter.WriteComment(reader.Value);
break;
case System.Xml.XmlNodeType.EndElement:
forkedWriter.WriteFullEndElement();
break;
}
return read;
}

The implementation of the other XmlReader's members will only reflect the operations being performed on the wrapped XmlReader.

Forking XML writing

To fork the writing operation of XML messages,I opted on building a XmlWriter derived class (XmlForkedWriter) thatwould receive, on instantiation of its objects, an instance of aXmlWriter derived class used to write the message and an instance of aXmlWriter derived class used to log the message.

For the same reasons as with the reader, I opted on implementing IDisposable.

 

public class XmlForkedReader : System.Xml.XmlReader, System.IDisposable
{
public XmlForkedWriter(System.Xml.XmlWriter writer,
System.Xml.XmlWriter forkedWriter) : base()
{

}
}

The implementation of this class is simplerthan the one for the reader because it only needs to duplicate theoperations being performed on the XmlForkedWriter in both the wrappedand forked XmlWriters.

Download Source

Twitter Digg Delicious Stumbleupon Technorati Facebook Email

No comments yet... Be the first to leave a reply!