Click here to Skip to main content
15,892,575 members
Articles / Programming Languages / XML
Article

XML forking in .NET Framework 1.1

Rate me:
Please Sign up or sign in to vote.
4.00/5 (5 votes)
25 Jan 20063 min read 29.6K   213   9  
An article on how to fork the processing of XML, in .NET Framework 1.1.

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 the disadvantage of not being able to take advantage of the SOAP implementation in the .NET framework but, because I had to do it myself, I would have some control over the messages.

Part of the message content belonged to the protocol implemented by the service provider (something like SOAP), and only a small part of it was service specific content. The service clients use DTOs (Data Transfer Objects) which were serialized using XmlSerializers.

To make it more difficult to tamper with message content and to gain some performance, I opted by not using XmlDocuments, instead, I used XmlReaders and XmlWriters.

Because XmlSerializers were being used, the need to log message content posed an interesting problem, because XmlReaders and XmlWriters were, some times, out of my control and under the control of XmlSerializers. Besides that, message logging could be optional, therefore, I needed something that I could turn "on" or "off" whenever I needed.

Forking XML processing

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

In the presence of such a task, I didn't refrain myself from getting help from the experts. Larry Serflaten pointed out that the concept I was looking for was message forking and Oleg 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) that would receive, on instantiation of its objects, an instance of a XmlReader derived class used to read the message and an instance of a XmlWriter derived class used to log the message.

I opted also to implement IDisposable to take advantage of the use of using (Using in Visual Basic) blocks and to be compliant with the implementation of the XmlReader in .NET framework 2.0.

C#
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 corresponding operation is performed on the wrapped XmlReader and the forked XmlWriter used to log the message.

C#
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) that would receive, on instantiation of its objects, an instance of a XmlWriter derived class used to write the message and an instance of a XmlWriter derived class used to log the message.

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

C#
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 simpler than the one for the reader because it only needs to duplicate the operations being performed on the XmlForkedWriter in both the wrapped and forked XmlWriters.

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here


Written By
Software Developer (Senior) Paulo Morgado
Portugal Portugal

Comments and Discussions

 
-- There are no messages in this forum --