Customized XML Reader Creation

Custom XML Readers allow an application to extend either the XmlReader, or XmlTextReader, or define its own custom readers.

Sample of Extending the XmlTextReader

The following is an example showing how to extend the XmlTextReader to create a reader that converts attributes to elements. The overridden Read provides the logic to track the current node type, and if an attribute, exposes this as an element node type using the XmlNodeType.Name and XmlNodeType.Value properties.

Option Explicit
Option Strict

Imports System
Imports System.IO
Imports System.Text
Imports System.Xml
Imports System.Xml.XPath

Public Class AttrToElementReader
   Inherits XmlTextReader

   Private _ReadAttributes As Boolean = False
   Private _ReadAttributeValue As Boolean = False
   Private _AttributeName As String = String.Empty
   Private _NodeType As XmlNodeType = XmlNodeType.None
   
   Public Sub New(fileName As String)
      MyBase.New(fileName)
      MyBase.WhitespaceHandling = WhitespaceHandling.None
      ' Intentionally Empty
   End Sub   
   
   Public Overrides Function Read() As Boolean
      If Not _ReadAttributes Then
         Dim baseRead As Boolean = MyBase.Read()
         _ReadAttributes = baseRead And XmlNodeType.Element = MyBase.NodeType And 0 < MyBase.AttributeCount
         Return baseRead
      End If
      
      'Read attribues;
      If _ReadAttributeValue Then
         _ReadAttributeValue = MyBase.ReadAttributeValue()
         If Not _ReadAttributeValue Then
            ' End of attribute.
' End element.
            _NodeType = XmlNodeType.EndElement
         Else
            _NodeType = XmlNodeType.None
         End If
         Return True
      End If
      
      _ReadAttributes = MyBase.MoveToNextAttribute()
      If _ReadAttributes Then
         _ReadAttributeValue = True
         _NodeType = XmlNodeType.Element
         _AttributeName = MyBase.Name
         Return True
      Else
         _ReadAttributeValue = False
         _NodeType = XmlNodeType.None
         _AttributeName = String.Empty
         Return Read()
      End If
   End Function
     
   
   Public Overrides ReadOnly Property NodeType() As XmlNodeType
      Get
         If XmlNodeType.None = _NodeType Then
            Return MyBase.NodeType
         End If
         
         Return _NodeType
      End Get
   End Property
      
   Public Overrides ReadOnly Property Value() As String
      Get
         If XmlNodeType.None = _NodeType Then
            Return MyBase.Value
         End If
         
         Return String.Empty
      End Get
   End Property
      
   Public Overrides ReadOnly Property Name() As String
      Get
         If XmlNodeType.None = _NodeType Then
            Return MyBase.Name
         End If
         
         Return _AttributeName
      End Get
   End Property
End Class 'AttrToElementReader
[C#]using System;
using System.IO;
using System.Text;
using System.Xml;
using System.Xml.XPath;

public class AttrToElementReader: XmlTextReader {    
    private bool         _ReadAttributes = false;
    private bool         _ReadAttributeValue = false;
    private string       _AttributeName = String.Empty;
    private XmlNodeType  _NodeType = XmlNodeType.None;
    
    public AttrToElementReader(String fileName) : base(fileName) {
        base.WhitespaceHandling = WhitespaceHandling.None;
        // Intentionally Empty.
    }

    public override bool Read() {
        if (!_ReadAttributes) {
            bool baseRead = base.Read();
            _ReadAttributes = (baseRead && XmlNodeType.Element == base.NodeType && 0 < base.AttributeCount);
            return baseRead;
        }

        // Reading attribues;
        if (_ReadAttributeValue) {
            _ReadAttributeValue = base.ReadAttributeValue();
            if (!_ReadAttributeValue) {
                // End of attribute.
// End element.
                _NodeType = XmlNodeType.EndElement;
            }
            else {
                _NodeType = XmlNodeType.None;
            }
            return true;
        }

        _ReadAttributes = base.MoveToNextAttribute();
        if (_ReadAttributes) {
            _ReadAttributeValue = true;
            _NodeType           = XmlNodeType.Element;
            _AttributeName      = base.Name;            
            return true;
        }
        else {
            _ReadAttributeValue = false;
            _NodeType           = XmlNodeType.None;
            _AttributeName      = String.Empty;
            return Read();
        }        
    }


    public override XmlNodeType NodeType {
        get {
            if (XmlNodeType.None == _NodeType) {
                return base.NodeType;
            }

            return _NodeType;
        }
    }

   public override String Value {
        get {
            if (XmlNodeType.None == _NodeType) {
                return base.Value;
            }

            return String.Empty;
        }
    }

   public override String Name {
        get {
            if (XmlNodeType.None == _NodeType) {
                return base.Name;
            }

            return _AttributeName;
        }
    }
 
}//AttrToElementReader

Chaining XmlReaders

The following example shows an implementation class, called XmlReaderReader, which is used as a class to chain XmlReaders together to provide a flow of readers, by delegating the calls. This is useful in creating readers that filter specific inputs and is a performant solution to specific parsing on XML.

A class derived from this XmlReaderReader can take any XmlReader as input. In the following example, the XmlCustomReader overloads the Read method and skips past price element nodes, thereby removing them from the stream.

' This sample demonstrate the ability to chain XmlReaders together by
' implementing an XmlReaderReader class which aggregates any given
' XmlReader and then delegates the calls to it
Namespace Test
   Public Class MyApp
      Public Shared Sub Main()
         Dim reader As New XmlTextReader("books.xml")
         Dim customreader As New XmlCustomReader(reader)
         customreader.ReadandWrite()
      End Sub
      
      Class XmlCustomReader
         Inherits XmlReaderReader
         
         Public Sub New(reader As XmlTextReader)
            MyBase.New(reader)
         End Sub
         
         Public Overrides Function Read() As Boolean
            Dim result As Boolean
            result = MyBase.Read()
            Select Case MyBase.NodeType
               Case XmlNodeType.Element
                  If MyBase.Name.Equals("price") Then
                     MyBase.Skip()
                  End If
               Case Else
            End Select
            Return result
         End Function
         
         Public Sub ReadandWrite()
            ' Read each node in the tree.
            While Read()
               Select Case NodeType
                  Case XmlNodeType.Element
                     Console.Write(("<" + Name))
                     While MoveToNextAttribute()
                        Console.Write((" " + Name + "='" + Value + "'"))
                     End While
                     Console.Write(">")
                  Case XmlNodeType.Text
                     Console.Write(Value)
                  Case XmlNodeType.CDATA
                     Console.Write(Value)
                  Case XmlNodeType.ProcessingInstruction
                     Console.Write(("<?" + Name + " " + Value + "?>"))
                  Case XmlNodeType.Comment
                     Console.Write(("<!--" + Value + "-->"))
                  Case XmlNodeType.Document
                     Console.Write("<?xml version='1.0'?>")
                  Case XmlNodeType.Whitespace
                     Console.Write(Value)
                  Case XmlNodeType.SignificantWhitespace
                     Console.Write(Value)
                  Case XmlNodeType.EndElement
                     Console.Write(("</" + Name + ">"))
               End Select
            End While
         End Sub
      End Class
      
      Public Class XmlReaderReader
         Inherits XmlTextReader
         Private _Reader As XmlTextReader
         
         Public Sub New(reader As XmlTextReader)
            _Reader = reader
         End Sub
         
         ' XmlReader methods and properties.
         Public Overrides ReadOnly Property NodeType() As XmlNodeType
            Get
               Return _Reader.NodeType
            End Get
         End Property
         
         Public Overrides ReadOnly Property Name() As [String]
            Get
               Return _Reader.Name
            End Get
         End Property
         
         Public Overrides ReadOnly Property LocalName() As [String]
            Get
               Return _Reader.LocalName
            End Get
         End Property
         
         Public Overrides ReadOnly Property NamespaceURI() As [String]
            Get
               Return _Reader.NamespaceURI
            End Get
         End Property
         
         Public Overrides ReadOnly Property Prefix() As [String]
            Get
               Return _Reader.Prefix
            End Get
         End Property
         
         Public Overrides ReadOnly Property HasValue() As Boolean
            Get
               Return _Reader.HasValue
            End Get
         End Property
         
         Public Overrides ReadOnly Property Value() As String
            Get
               Return _Reader.Value
            End Get
         End Property
         
         Public Overrides ReadOnly Property Depth() As Integer
            Get
               Return _Reader.Depth
            End Get
         End Property
         
         Public Overrides ReadOnly Property BaseURI() As String
            Get
               Return _Reader.BaseURI
            End Get
         End Property
         
         Public Overrides ReadOnly Property IsEmptyElement() As Boolean
            Get
               Return _Reader.IsEmptyElement
            End Get
         End Property
         
         Public Overrides ReadOnly Property IsDefault() As Boolean
            Get
               Return _Reader.IsDefault
            End Get
         End Property
         
         Public Overrides ReadOnly Property QuoteChar() As Char
            Get
               Return _Reader.QuoteChar
            End Get
         End Property
         
         Public Overrides ReadOnly Property XmlSpace() As XmlSpace
            Get
               Return _Reader.XmlSpace
            End Get
         End Property
         
         
         Public Overrides ReadOnly Property XmlLang() As String
            Get
               Return _Reader.XmlLang
            End Get
         End Property
         
         Public Overrides ReadOnly Property AttributeCount() As Integer
            Get
               Return _Reader.AttributeCount
            End Get
         End Property
         
         Overloads Public Overrides Function GetAttribute(name As String) As String
            Return _Reader.GetAttribute(name)
         End Function
         
         Overloads Public Overrides Function GetAttribute(name As String, namespaceURI As String) As String
            Return _Reader.GetAttribute(name, namespaceURI)
         End Function
         
         Overloads Public Overrides Function GetAttribute(i As Integer) As String
            Return _Reader.GetAttribute(i)
         End Function
         
         Default Public Overrides Overloads ReadOnly Property Item(i As Integer) As String
            Get
               Return _Reader(i)
            End Get
         End Property
         
         Default Public Overrides Overloads ReadOnly Property Item(name As String) As String
            Get
               Return _Reader(name)
            End Get
         End Property
         
         Default Public Overrides Overloads ReadOnly Property Item(name As String, namespaceURI As String) As String
            Get
               Return _Reader(name, namespaceURI)
            End Get
         End Property
         
         Overloads Public Overrides Function MoveToAttribute(name As String) As Boolean
            Return _Reader.MoveToAttribute(name)
         End Function
         
         Overloads Public Overrides Function MoveToAttribute(name As String, ns As String) As Boolean
            Return _Reader.MoveToAttribute(name, ns)
         End Function
         
         Overloads Public Overrides Sub MoveToAttribute(i As Integer)
            _Reader.MoveToAttribute(i)
         End Sub
         
         Public Overrides Function MoveToFirstAttribute() As Boolean
            Return _Reader.MoveToFirstAttribute()
         End Function
         
         Public Overrides Function MoveToNextAttribute() As Boolean
            Return _Reader.MoveToNextAttribute()
         End Function
         
         Public Overrides Function MoveToElement() As Boolean
            Return _Reader.MoveToElement()
         End Function
         
         ' This is the only place that needs to be changed.
         Public Overrides Function Read() As Boolean
            Return _Reader.Read()
         End Function
         
         Public Overrides ReadOnly Property EOF() As Boolean
            Get
               Return _Reader.EOF
            End Get
         End Property
         
         Public Overrides Overloads Sub Close()
            _Reader.Close()
         End Sub
         
         Public Overrides ReadOnly Property ReadState() As ReadState
            Get
               Return _Reader.ReadState
            End Get
         End Property
         
         Public Overrides Function ReadString() As String
            Return _Reader.ReadString()
         End Function
         
         Public Overrides Function ReadInnerXml() As String
            Return _Reader.ReadInnerXml()
         End Function
         
         Public Overrides Function ReadOuterXml() As String
            Return _Reader.ReadOuterXml()
         End Function
         
         Public Overrides ReadOnly Property NameTable() As XmlNameTable
            Get
               Return _Reader.NameTable
            End Get
         End Property
         
         Public Overrides Function LookupNamespace(prefix As String) As String
            Return _Reader.LookupNamespace(prefix)
         End Function
         
         Public Overrides Overloads Sub ResolveEntity()
            _Reader.ResolveEntity()
         End Sub
         
         Public Overrides Function ReadAttributeValue() As Boolean
            Return _Reader.ReadAttributeValue()
         End Function
      End Class
   End Class
End Namespace
[C#]using System;
using System.Xml;

// This sample demonstrate the ability to chain XmlReaders together by
// implementing an XmlReaderReader class which aggregates any given
// XmlReader and then delegates the calls to it.

namespace Test
{
    public class MyApp 
    {
        public static void Main()
      {
         XmlTextReader reader = new XmlTextReader("books.xml");
         XmlChainingReader customreader = new XmlChainingReader(reader);
         customreader.ReadandWrite();

      }

      class XmlChainingReader : XmlReaderReader
      {
          public XmlChainingReader (XmlReader reader) : base (reader)
         {}

         public override bool Read ()
         {
            bool result;
            result = base.Read();

            switch (base.NodeType)
            {
            case XmlNodeType.Element:
              if (base.Name.Equals("price"))
                 base.Skip();
            break;

            default:
               break;
            }
            return result;
         }
      
         public void ReadandWrite()
         {
            // Read each node in the tree.
            while (Read())
            {
               switch (NodeType)
               {
                 case XmlNodeType.Element:
                  Console.Write("<" + Name);
                  while (MoveToNextAttribute())
                    Console.Write(" " + Name + "='" + Value + "'");
                  Console.Write(">");
                  break;
                 case XmlNodeType.Text:
                  Console.Write(Value);
                  break;
                 case XmlNodeType.CDATA:
                  Console.Write(Value);
                  break;
                 case XmlNodeType.ProcessingInstruction:
                  Console.Write("<?" + Name + " " + Value + "?>");
                  break;
                 case XmlNodeType.Comment:
                  Console.Write("<!--" + Value + "-->");
                  break;
                 case XmlNodeType.Document:
                  Console.Write("<?xml version='1.0'?>");
                  break;
                 case XmlNodeType.Whitespace:
                  Console.Write(Value);
                  break;
                 case XmlNodeType.SignificantWhitespace:
                  Console.Write(Value);
                  break;
                 case XmlNodeType.EndElement:
                  Console.Write("</" + Name + ">");
                  break;
               }
            }
         }
      }

      public class XmlReaderReader : XmlReader
      {
        
         XmlReader _Reader;

         public XmlReaderReader (XmlReader reader )
         {
            _Reader = reader;
         }

         // XmlReader methods and properties.

         public override XmlNodeType NodeType
         {
            get { return _Reader.NodeType; }
         }

         public override String Name
         {
            get { return _Reader.Name; }            
         }

         public override String LocalName
         {
            get { return _Reader.LocalName; }            
         }

         public override String NamespaceURI
         {
            get { return _Reader.NamespaceURI; }            
         }

         public override String Prefix
         {
            get { return _Reader.Prefix; }            
         }


         public override bool HasValue 
         { 
            get { return _Reader.HasValue; }           
         }             

         public override string Value
         {
            get { return _Reader.Value; }            
         }

         public override int Depth
         {
            get { return _Reader.Depth; }            
         }

         public override string BaseURI
         {
            get { return _Reader.BaseURI; }            
         }

         public override bool IsEmptyElement
         {
            get { return _Reader.IsEmptyElement; }            
         }

         public override bool IsDefault
         {
            get { return _Reader.IsDefault; }            
         }

         public override char QuoteChar
         {
            get { return _Reader.QuoteChar; }            
         }

         public override XmlSpace XmlSpace
         {
            get { return _Reader.XmlSpace; }            
         }

         public override string XmlLang
         {
            get { return _Reader.XmlLang; }            
         }


         public override int AttributeCount
         {
            get { return _Reader.AttributeCount; }            
         }

         public override string GetAttribute(string name)
         {
            return _Reader.GetAttribute( name );
         }

         public override string GetAttribute(string name, string namespaceURI) 
         {
            return _Reader.GetAttribute( name, namespaceURI );
         }

         public override string GetAttribute(int i) 
         {
            return _Reader.GetAttribute( i );
         }

         public override string this [ int i ] 
         {             
            get { return _Reader[ i ]; }
         }
        
         public override string this [ string name ]
         { 
            get { return _Reader[ name ]; }
         }

         public override string this [ string name,string namespaceURI ] 
         { 
            get { return _Reader[ name, namespaceURI ]; }
         }
        
         public override bool MoveToAttribute(string name) 
         {
            return _Reader.MoveToAttribute( name );
         }

         public override bool MoveToAttribute(string name, string ns)
         {
            return _Reader.MoveToAttribute( name, ns );
         }

         public override void MoveToAttribute(int i)
         {
            _Reader.MoveToAttribute( i );
         }

         public override bool MoveToFirstAttribute()
         {
            return _Reader.MoveToFirstAttribute();
         }

         public override bool MoveToNextAttribute()
         {
            return _Reader.MoveToNextAttribute();
         }

         public override bool MoveToElement()
         {
            return _Reader.MoveToElement();
         }

         //
         // This is the only place that needs to be changed.
         //
         public override bool Read()
         {
            return _Reader.Read();
         }

         public override bool EOF 
         { 
            get { return _Reader.EOF; }
         }
        
         public override void Close() 
         {
            _Reader.Close();
         }

         public override ReadState ReadState
         { 
            get { return _Reader.ReadState; }
         }

         public override string ReadString()
         { 
            return _Reader.ReadString();
         }

         public override string ReadInnerXml()
         { 
            return _Reader.ReadInnerXml();
         }

         public override string ReadOuterXml()
         { 
            return _Reader.ReadOuterXml();
         }

         public override XmlNameTable NameTable 
         { 
            get { return _Reader.NameTable; }
         }
        
         public override string LookupNamespace(string prefix) 
         {
            return _Reader.LookupNamespace( prefix );
         }

         public override void ResolveEntity() 
         {
            _Reader.ResolveEntity();
         }

         public override bool ReadAttributeValue()
         {
            return _Reader.ReadAttributeValue();
         }
      }

   }

}

See Also

Reading XML with the XmlReader | Reading XML Data with XmlTextReader | Reading Node Trees with XmlNodeReader | Validating XML with XmlValidatingReader | XmlReader Class | XmlReader Members | XmlNodeReader Class | XmlNodeReader Members | XmlTextReader Class | XmlTextReader Members | XmlValidatingReader Class | XmlValidatingReader