XML sub-tree processing

Introduction

This article will show you how to process XML sub-trees when using XmlReaders and XmlWriters in the .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 the messages were being handed out toXmlSerializers (and instances of other classes), I was afraid somethingcould happen to the message content because XmlReaders and XmlWriterswere, some times, out of my control. I had to build something thatwould guarantee the integrity of the messages (and the XmlReaders andXmlWriters being used to process them).

XML sub-tree processing

The requirement looked simple; guarantee thateach element of the XmlReader or XmlWriter was treated as a sub-treeand seen by other code as a complete XML document.

XML sub-tree reading

To implement XML sub-tree reading, I opted onbuilding a XmlReader derived class (XmlSubtreeReader) that wouldreceive, on instantiation of its objects, an instance of an XmlReaderderived class from where its current element will be used as the rootof a new XmlReader.

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 the .NETframework 2.0.

public class XmlSubtreeReader : System.Xml.XmlReader,
System.IDisposable
{
public XmlSubtreeReader(System.Xml.XmlReader
wrappedReader) : base()
{

}
}

To guarantee that the recently createdXmlSubtreeReader doesn't compromise the integrity of the wrappedXmlReader, on its instantiation, the wrapped XmlReader's current depthis stored and used to guarantee that the wrapped XmlReader's depthdoesn't go bellow its initial value.

public override bool Read()
{
switch (this.readerState)
{
case XmlSubtreeReader.State.Initial:
{
this.readerState =
XmlSubtreeReader.State.Interactive;
this.ProcessNamespaces();
return true;
}
case XmlSubtreeReader.State.Interactive:
{
break;
}
case XmlSubtreeReader.State.PopNamespaceScope:
{
this.namespaceManager.PopScope();
goto case
XmlSubtreeReader.State.ClearNamespaceAttributes;
}
case XmlSubtreeReader.State.ClearNamespaceAttributes:
{
this.namespaceAttributeCount = 0;
this.readerState =
XmlSubtreeReader.State.Interactive;
break;
}
case XmlSubtreeReader.State.Error:
case XmlSubtreeReader.State.EndOfFile:
case XmlSubtreeReader.State.Closed:
default:
{
return false;
}
}
this.currentNamespaceAttribute = -1;
this.wrappedReader.MoveToElement();
if ((this.wrappedReader.Depth == this.initialDepth) &&
((this.wrappedReader.NodeType ==
System.Xml.XmlNodeType.EndElement)
|| ((this.wrappedReader.NodeType ==
System.Xml.XmlNodeType.Element)
&& this.wrappedReader.IsEmptyElement)))
{
this.readerState = XmlSubtreeReader.State.EndOfFile;
return false;
}
if (this.wrappedReader.Read())
{
this.ProcessNamespaces();
return true;
}
return false;
}

On the other hand, when closing theXmlSubtreeReader, the wrapped XmlReader should be skipped until itsinitial depth is reached.

public override void Close()
{
if (this.readerState !=
XmlSubtreeReader.State.Closed)
{
while (this.readerState !=
XmlSubtreeReader.State.EndOfFile)
{
this.Skip();
}
this.wrappedReader.Skip();
this.readerState = XmlSubtreeReader.State.Closed;
}
}

XML sub-tree writing

To implement XML sub-tree writing, I opted onbuilding a XmlWriter derived class (XmlSubtreeWriter) that wouldreceive, on instantiation of its objects, an instance of an XmlWriterderived class from where its current element will be used as the rootof a new XmlWriter.

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

public class XmlSubtreeWriter : System.Xml.XmlReader,
System.IDisposable
{
public XmlSubtreeWriter(System.Xml.XmlWriter
wrappedWriter) : base()
{

}
}

To guarantee that the recently createdXmlSubtreeWriter doesn't compromise the integrity of the wrappedXmlWriter, on its instantiation, the wrapped XmlWriter's current depthis stored and used to guarantee that the wrapped XmlWriter's depthdoesn't go bellow its initial value.

public override void WriteStartElement(string prefix,
string localName, string ns)
{
CheckWrite();
this.depth++;
this.wrappedWriter.WriteStartElement(prefix, localName, ns);
this.currentState = this.wrappedWriter.WriteState;
}

The CheckWrite method is called at thebeginning of every write start method to validate if theXmlSubtreeWriter is in a valid state for writing.

In the same way, the CheckWriteEndElementmethod is called in the beginning of every write end method to validateif the XmlSubtreeWriter has reached its root.

As in sub-tree reading, when closing theXmlSubtreeWriter, the wrapped XmlWriter should end every element untilits initial depth is reached.

public override void Close()
{
this.CompleteAll();
this.currentState = System.Xml.WriteState.Closed;
this.Flush();
}

Download Source

Related Articles :

Twitter Digg Delicious Stumbleupon Technorati Facebook Email

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