Click here to Skip to main content
15,893,814 members
Articles / Programming Languages / XML

Towards a Declarative SAX Framework : Part 1 - A Simple SAX-to-C#-Mapping

Rate me:
Please Sign up or sign in to vote.
4.51/5 (15 votes)
20 May 200422 min read 63K   894   33  
This article demonstrates a simple but flexible way to read XML content without DOM
#region Copyright
/*********************************************************************************
 * Copyright (c) 2004 by Martin Friedrich										 *
 * Permission to freely distribute, modify and compile these sources and		 *
 * binaries as long as this copyright notice is included.						 *
 * Use of code on your risk!													 *
 * *******************************************************************************/
#endregion

using System;
using System.Collections;
using System.Xml;

namespace SaxParserSupport
{
	namespace Impl 
	{

		/// <summary>
		/// <tt>SaxParserImpl</tt> implements the <tt>SaxParser</tt> interface. It
		/// uses a <tt>System.Xml.XmlTextReader</tt> instance to do the actual parsing.
		/// </summary>
		public class SaxParserImpl : SaxParser
		{
			/// <summary>
			/// Creates a new instance of <tt>SaxParserImpl</tt> from an existing 
			/// <tt>XmlReader</tt> instance.
			/// </summary>
			/// <param name="src">The <tt>XmlReader</tt> instance to used for parsing. This might
			/// be a validating <tt>XmlReader</tt>.</param>
			public SaxParserImpl( System.Xml.XmlReader src )
			{
				_source = src;
			}

			/// <summary>
			/// Creates a new instance of <tt>SaxParserImpl</tt> which will read from an existing stream. 
			/// This stream must support reading.
			/// </summary>
			/// <remarks>
			/// The <tt>XmlReader</tt> instance created internally will not do validation. To support
			/// validation, use class <tt>SaxParserSupport.Impl.SaxValidatingParser</tt>tt>
			/// instead.
			/// </remarks>
			/// <param name="src">The stream to be created from.</param>
			/// <exception cref="System.NotSupportedException">Thrown if the stream doesn't support
			/// reading</exception>
			public SaxParserImpl( System.IO.Stream src )
			{
				_source = createXmlReader( src );
			}

			/// <summary>
			/// Parses XML data from stream <tt>src</tt>.
			/// </summary>
			/// <exception cref="System.NotSupportedException">Thrown if <tt>src</tt> does
			/// not support read operations.</exception>
			/// <exception cref="System.InvalidOperationException">Thrown if the 
			/// <tt>RootElement</tt>-property is not set.</exception>
			/// <exception cref="System.Xml.XmlException">Thrown by <tt>XmlReader</tt> 
			/// instance if XML content is invalid.</exception>
			/// <remarks>In addition to the exceptions mentioned above, this method may
			/// throw any IO-related exception.</remarks>
			public void parse( System.IO.Stream src ) 
			{

				if ( !src.CanRead ) throw new System.NotSupportedException( "read operation on stream " 
										+ "not allowed" );
				_source = createXmlReader( src );

				parse();
			}

			/// <summary>
			/// Parses the XML data from the currently set <i>XmlReader</i>. The 
			/// <tt>XmlReader</tt> is set directly at creation or indirectly by
			/// <tt>SaxParserSupport.SaxParserImpl.parse(System.IO.Stream)</tt> or
			/// the appropriate ctor.
			/// </summary>
			/// <exception cref="System.InvalidOperationException">Thrown if the 
			/// <tt>RootElement</tt>-property is not set.</exception>
			/// <exception cref="System.Xml.XmlException">Thrown by <tt>XmlReader</tt> 
			/// instance if XML content is invalid.</exception>
			/// <remarks>In addition to the exceptions mentioned above, this method may
			/// throw any IO-related exception.</remarks>
			public void parse() 
			{
				if ( _elementStack.Count != 0 )
					throw new System.InvalidOperationException( "element stack is not empty" );


				bool startdocument = true;
				while( _source.Read() ) 
				{
					switch ( _source.NodeType ) 
					{
						case System.Xml.XmlNodeType.Attribute:
							((XMLSimpleElement )_elementStack.Peek()).setAttribute( _source.Name, _source.Value );
							break;
						case System.Xml.XmlNodeType.Element:
							if ( startdocument ) 
							{
								startdocument = false;
								RootElement.setName( _source.Name );
							} 
							else 
							{
								XMLSimpleElement top = (XMLSimpleElement )_elementStack.Peek();
								if ( top is XMLElement ) 
								{
									XMLElement top2 = (XMLElement )top;
									XMLSimpleElement child = top2.lookupElement( _source.NamespaceURI + ":" + _source.LocalName );
									child.setName( _source.Name );
									child.setOwner( top2 );
									_elementStack.Push( child );
								}
							}
							XMLSimpleElement current = (XMLSimpleElement )_elementStack.Peek();
							int n = _source.AttributeCount;
							for ( int i = 0; i < n; i ++ ) 
							{
								_source.MoveToAttribute( i );
								current.setAttribute( _source.Name, _source.Value );
							}
							_source.MoveToElement();
//							if ( ! ( current is XMLElement ) ) 
							if ( _source.IsEmptyElement )
							{
								current.onElementEnd( current.getOwner() );
								_elementStack.Pop();
							}
							break;
						case System.Xml.XmlNodeType.EndElement:
						{
							XMLSimpleElement top = (XMLSimpleElement )_elementStack.Peek();
							top.onElementEnd( top.getOwner() );
							_elementStack.Pop();
						}
							break;
						case System.Xml.XmlNodeType.Text:
							((XMLElement )_elementStack.Peek()).onText( _source.Value );
							break;
						case System.Xml.XmlNodeType.Whitespace:
							((XMLElement )_elementStack.Peek()).onWhitespace( _source.Value );
							break;
						case System.Xml.XmlNodeType.SignificantWhitespace:
							((XMLElement )_elementStack.Peek()).onSignificantWhitespace( _source.Value );
							break;
						case System.Xml.XmlNodeType.XmlDeclaration:
							if ( RootElement == null ) throw  new System.InvalidOperationException( "no root element \'"
														   + _source.Name + " \' is given" );
							else _elementStack.Push( RootElement );
							int m = _source.AttributeCount;
							for ( int i = 0; i < m; i ++ ) 
							{
								_source.MoveToAttribute( i );
								switch ( _source.Name ) 
								{
									case "version":
										RootElement.setVersion( _source.Value );
										break;
									case "encoding":
										RootElement.setEncoding( _source.Value );
										break;
									default:
										break;
								}
							}
							_source.MoveToElement();
							break;
					}
				}
			}

			/// <summary>
			/// Creates a <tt>XmlReader</tt> from a stream to be used for XML parsing.
			/// To change characteristics of the <tt>XmlReader</tt> used, override this
			/// method.
			/// <seealso cref="SaxParserSupport.Impl.SaxValidatingParserImpl"/>
			/// </summary>
			/// <param name="input">The stream to be read from</param>
			/// <returns>An instance of class <tt>System.Xml.XmlTextReader</tt></returns>
			protected virtual System.Xml.XmlReader createXmlReader( System.IO.Stream input )
			{
				return new XmlTextReader( input );
			}

			/// <summary>
			/// The <tt>XMLElement</tt> instance representing the root element
			/// of the XML content.
			/// </summary>
			public SaxParserSupport.XMLDocument RootElement
			{
				get
				{
					return _rootelement;
				}
				set
				{
					_rootelement = value;
				}
			}

			/// <summary>
			/// ***PRIVATE***
			/// </summary>
			private XmlReader _source;

			/// <summary>
			/// ***PRIVATE***
			/// </summary>
			private Stack _elementStack = new Stack();

			/// <summary>
			/// ***PRIVATE***
			/// </summary>
			private XMLDocument _rootelement;

		}
	}
}

By viewing downloads associated with this article you agree to the Terms of Service and the article's licence.

If a file you wish to view isn't highlighted, and is a text file (not binary), please let us know and we'll add colourisation support for it.

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
Web Developer
Germany Germany

Still lacking an university degree in computer science, I have 20 years of experience in software development and implementation. Having expert knowledge in object-oriented programming languages like C++, Java and C# on Windows, LINUX and UNIX platforms, I participated in multiple research projects at the University of Oldenburg. During these assignments, I was trusted with implementation of a graphical editor for specification languages like CSP or Z and a prototypical tool for workflow data distribution and analysis. I gained experiences in a widespread spectrum of CS and software development topics, ranging from compiler construction across data base programming to MDA. My research interests include questions of graphical user interface design and component-based systems.


I consider myself an old-school CS geek. While I admit that simple tasks do not pose much of a problem and can be done in quick and efficient manner, it's the challenging ones that appeal to me. If you are looking for a skillful employee - be it on a permanent, contract or freelance basis - and if you can live with a lacking university degree, why not contacting me?


Comments and Discussions