KnowDotNet Visual Organizer

Wrapping the XML Dom and XML Text Writer

Write Reusable Code - Don't Reinvent the Wheel

by Les Smith
Print this Article Discuss in Forums

I am constantaly amazed at the amount of .NET code that gets written compared to the amout of that code which was written so that someone else could reuse it instead of re-inventing the same code over and over.  A prime example of this is in building XML packets to be be passed to an XML Web Service and writing code to analyze the XML response from the Web Service.  Some developers may have every method of the XML Dom and XmlTextWriter memorized, but I don't.  However I do know the difference between an element and an attribute, and that is all I would like to have to keep up with when I want to create XML or read data from an XML string.

This article will present a simple XML Decarator or Wrapper for the Dom and the XmlTextWriter.  All the code is in C#, it is a simple task to rewrite it in VB.NET if you are a VB.NET programmer.  First, I will show you the wrapper code class as follows.

// This class contains both creation and retrieval methods for
// easing the pain of using the XML DOM.  Only requirement is that
// the user understand the heirarchy of Elements and Attibutes in XML.

using System;
using System.Xml;
using System.Data;
using System.IO;
using System.Text;

namespace XMLWrap
{
  
public class XMLDocWrapper
   {

      #region  Private Variables
  
      
private XmlDocument doc;
      
private XmlTextWriter xtw;
      
private MemoryStream memoryStream;
      
private string xmlString;
      
public string XmlString
      {
        
get{return xmlString;}
        
set{xmlString = value;}
      }
      #endregion

       #region
  Public Writer Methods

      
// Opens xml text writer with the passed root element
     public void CreateDocWithRoot(string rootName)
      {
          xtw.Formatting = Formatting.Indented;
          xtw.Indentation = 3;
          xtw.WriteStartDocument(
true);
          xtw.WriteStartElement(rootName);
      }
      
// Add Attribute to current open element.
     public void AddAttribute(string attrName, string attrValue)
      {
          xtw.WriteAttributeString(attrName, attrValue);
      }
      
// Open new element with eltName.
     public void OpenNewElement(string eltName)
      {
          xtw.WriteStartElement(eltName);
      }
      
// Close the currently open element.
     public void CloseCurrentElement()
      {
          xtw.WriteEndElement();
      }
      
// Add a complete element with one attribute.
     public void AddCompleteElementWithAttribute(string eltName, string attrName, string attrValue)
      {
          xtw.WriteStartElement(eltName);
          xtw.WriteAttributeString(attrName, attrValue);
          xtw.WriteEndElement();
      }
      
// Builds element with innertext.
     public void AddCompleteElmentWithInnerText(string eltName, string innerText)
      {
          xtw.WriteElementString(eltName, innerText);
      }
      
// Returns completed XML string
     public string CloseDoc()
      {
        
// Use a StreamReader to display the result.
         xtw.WriteEndElement();
        
// End the document.
         xtw.WriteEndDocument();
          xtw.Flush();
          StreamReader stream_reader =
new StreamReader(memoryStream);

          memoryStream.Seek(0, SeekOrigin.Begin);

        
string strBuild = stream_reader.ReadToEnd();

        
// Close the XmlTextWriter.
         xtw.Close();

        
return strBuild;
      }

      #endregion

       #region
  Public Retrieval Methods
      
// Loads passed xml into document object.
     public bool LoadXMLString(string xml)
      {
        
try
        {
             doc.LoadXml(xml);
            
return true;
         }
        
catch
        {
            
return false;
         }
      }
      
//Returns true if requested element is extant, else returns false.
     private bool IsElementExtant(string xml, string rptEltName)
      {
          doc =
new XmlDocument();
          doc.LoadXml(xml);
          XmlNode node =
null;
        
try
        {
             node = doc.SelectSingleNode("//" + rptEltName);
         }
        
catch
        {
            
return false;
         }
        
if (node != null)
            
return true;
        
else
           return false;
      }
      
// returns innertext of element named in xPath.
     public string GetElementText(string xml, string xPath)
      {
          doc.LoadXml(xml);
          XmlNode node = doc.SelectSingleNode(xPath);
        
if (node != null)
         {
            
return node.InnerText;
         }
        
else
        {
            
return string.Empty;
         }
      }

      
// Return attribute value specified by xpath
     public string AttributeValueFromXML(string xml, string xpath)
      {
          XmlDocument doc =
new XmlDocument();
          doc.LoadXml(xml);
          XmlNode xn = doc.SelectSingleNode(xpath);

        
string value = xn.Value.ToLower();
        
return value;

      }

      #endregion

       #region
  Constructor

      
// Constructor opens memorystream and textwriter.
     public XMLDocWrapper()
      {
          memoryStream =
new MemoryStream();
          xtw =
new XmlTextWriter(memoryStream, Encoding.UTF8);
          doc =
new XmlDocument();
      }

      #endregion
  }
}

Next, I will create a simple method to call some of the methods of the wrapper.  You will notice that you do not have to understand the methods of the XML Dom (Document Object Model), nor the methods of the XmlTextWriter.  Obviously, the class shown above is not exhaustive, and I hope to add some methods to it in a later article to provide the ability to loop through multiple elements of the same name, retrieving elements and/or attributes from them.  But for now, I am just making a point about reusability and simplifying your daily coding efforts by writing code that you can use without knowing every method in the Xml namespace.  The method shown below will call the class shown above.

  
private void TextXmlPrimitives()
   {
        // create a simple xml document
        Console.WriteLine("-------- Created XML String ----------");
      XMLWrap.XMLDocWrapper xd =
new XMLWrap.XMLDocWrapper();

      xd.CreateDocWithRoot("MY_XML_ROOT_ELEMENT");
      xd.AddAttribute("ROOT_ELEMENT_ATTRIBUTE_ONE", "RootAttr1Value");
      xd.AddAttribute("ROOT_ELEMENT_ATTRIBUTE_TWO", "RootAttrValue2");
      xd.OpenNewElement("FIRST_CHILD_ELEMENT");
      xd.AddAttribute("User_ID", "Les Smith");
      xd.AddAttribute("Password", "$!lsmith_5");
      xd.CloseCurrentElement();
      xd.AddCompleteElementWithAttribute("SECOND_CHILD_ELEMENT", "ATTR_NAME", "Value2");
      xd.AddCompleteElmentWithInnerText("THIRD_ELEMENT", "TEXT");
      
string xml = xd.CloseDoc();
      Console.WriteLine(xml);
        Console.WriteLine("--------Retrieval Values ----------");
      
// now get the data back from the wrapper
      
// get the first attribute of the root element
      Console.WriteLine(xd.AttributeValueFromXML(xml, "MY_XML_ROOT_ELEMENT/@ROOT_ELEMENT_ATTRIBUTE_ONE"));
      
// get the innertext from third child element
      Console.WriteLine(xd.GetElementText(xml, "MY_XML_ROOT_ELEMENT/THIRD_ELEMENT"));
   }

The output of the shown above is shown next.  The Xml string is printed first, followed by the retrieval output, represented by the last two lines in the output window.

-------- Created XML String ----------
<?xml version="1.0" encoding="utf-8" standalone="yes"?>
<MY_XML_ROOT_ELEMENT ROOT_ELEMENT_ATTRIBUTE_ONE="RootAttr1Value" ROOT_ELEMENT_ATTRIBUTE_TWO="RootAttrValue2">
   <FIRST_CHILD_ELEMENT User_ID="Les Smith" Password="$!lsmith_5" />
   <SECOND_CHILD_ELEMENT ATTR_NAME="Value2" />
   TEXT

--------Retrieval Values ----------
rootattr1value
TEXT

Again, all I have to know remember about Xml writing and reading is the difference between an Element and an Attribute.  If you don't know that, then you need to do a little research at some tutorial site such as
this link.

Writing Add-Ins for Visual Studio .NET
Writing Add-ins for Visual Studio .NET
by Les Smith
Apress Publishing