65.9K
CodeProject is changing. Read more.
Home

Insert XML Nodes Using XmlTextReader and XmlTextWriter

starIconstarIconstarIcon
emptyStarIcon
starIcon
emptyStarIcon

3.65/5 (15 votes)

Jan 30, 2004

2 min read

viewsIcon

162755

downloadIcon

4506

Using XmlTextReader and XmlTextWriter to insert an XML document within another document without creating an XmlDocument object

Introduction

XmlDocument objects are easy to use for navigating the DOM and copying, modifying, or inserting nodes. However, they can also use a large amount of memory to store the entire DOM of a large XML string in memory. Because of this, the XmlReader and XmlWriter classes are used for stream based manipulation of an XML string.

Using the code

The insert method illustrates the process of reading two strings of XML with the XmlTextReader from the System.Xml namespace to ensure they are both valid XML. The source XML is read to ensure its validity and then stored in a StringBuilder object. The target XML is read, but the root element is stripped off and the name and attributes and stored in string variables. The inner XML from the target document is read and stored in another StringBuilder object. Finally the XmlTextWriter is used to write out the root element and its attributes with the source XML inserted before the inner XML from the target document.

Start off with the insert method declaration.

private string insert(string sourceXml, string targetXml)
{
...
}

First, the XmlTextReader and XmlTextWriter objects must be created and initialized with the sourceXml string.

// Declare and set readers and writers for sourceXml
System.Text.StringBuilder sb = new System.Text.StringBuilder();
System.IO.StringReader stringReader = 
  new System.IO.StringReader(sourceXml);
System.IO.StringWriter stringWriter = 
  new System.IO.StringWriter(sb);

System.Xml.XmlTextReader xmlReader = 
  new System.Xml.XmlTextReader(stringReader);
System.Xml.XmlTextWriter xmlWriter = 
  new System.Xml.XmlTextWriter(stringWriter);

string strValidSourceXml = String.Empty;
string strValidTargetXml = String.Empty;

string strRootName = String.Empty;
string[,] arrRootAtts = null;

The source XML is read, validated, and written into the StringBuilder object by parsing each node with the XmlReader and subsequently writing each node to the StringBuilder with the XmlWriter.

try
{
  while(xmlReader.Read())
  {
    xmlWriter.WriteNode(xmlReader,true);
  }
}
catch(System.Xml.XmlException e)
{
  MessageBox.Show(this,
    "Error parsing source XML\n\nMessage: " + e.Message,
    "Parser Error",MessageBoxButtons.OK,MessageBoxIcon.Error);
  return String.Empty;
}
strValidSourceXml = sb.ToString();

After reading the source, the readers and writers must be reset to parse the target XML.

// Reset readers and writers for targetXml
sb = new System.Text.StringBuilder();
stringReader = new System.IO.StringReader(targetXml);
stringWriter = new System.IO.StringWriter(sb);
xmlReader = new System.Xml.XmlTextReader(stringReader);
xmlWriter = new System.Xml.XmlTextWriter(stringWriter);

Then the root element is broken into its components - name and attributes. The inner XML from the root is read into a string variable.

try
{
  while(xmlReader.Read())
  {
    // Parse root node and store name and attributes
    strRootName = xmlReader.Name;
    if(xmlReader.HasAttributes)
    {
      int i = 0;
      arrRootAtts = new string[xmlReader.AttributeCount,2];
      while(xmlReader.MoveToNextAttribute())
      {
        arrRootAtts[i,0] = xmlReader.Name;
        arrRootAtts[i,1] = xmlReader.Value;
        i++;
      }
    }
    // Store inner XML as string
    strValidTargetXml = xmlReader.ReadInnerXml();
  }
}
catch(System.Xml.XmlException e)
{
  MessageBox.Show(this,
    "Error parsing target XML\n\nMessage: " + e.Message,
    "Parser Error",MessageBoxButtons.OK,MessageBoxIcon.Error);
  return String.Empty;
}

The root node and its attributes are written out to a StringBuilder using the XmlWriter. The validated XML from the source and the inner XML from the target are written as children of the root node.

// Combine root node with source and target XML strings
xmlWriter.WriteStartElement(strRootName);
if (arrRootAtts != null)
{
  for (int i = 0; i < arrRootAtts.GetLength(0); i++)
  {
    xmlWriter.WriteAttributeString(arrRootAtts[i,0],
      arrRootAtts[i,1]);
  }
}
xmlWriter.WriteRaw(strValidTargetXml + strValidSourceXml);
xmlWriter.WriteEndElement();

Finally, the method returns the string from the StringBuilder object containing the combined XML as a string.

return sb.ToString();

Memory Savings

Be aware that the StringBuilder and the strings themselves carry some memory overhead, so it's often worth taking time to compare the two approaches for typical strings of XML and determine if the memory savings is worth the extra development effort.

In my testing, for combining two strings of XML, using the XmlReader resulted in 1/2 the memory consumption of the XmlDocument approach.

While the XmlDocument object is certainly more intuitive for inserting nodes into a string of XML, the XmlReader and XmlWriter objects can be utilized for quickly parsing and combining large strings of XML with less memory overhead than using the XmlDocument and XmlDocumentFragment objects to combine two documents.

History

  • Posted - 1/29/2004