/// <summary>
 /// Creates an XML document from a row tree and writes it to a stream using UTF-8 encoding.
 /// </summary>
 // If we need to start generating XML on the fly for HTTP responses, it may make sense to create an overload of this method that takes an HttpResponse
 // object instead of a stream. This new overload would use the HttpResponse.Output property to obtain a TextWriter and then create the XmlWriter on top of
 // that instead of a stream. The advantage of this approach is that the encoding of the XML would then be determined by ASP.NET, which takes into account
 // any request headers the client may have sent that pertain to the desired encoding of the response.
 public static void CreateXmlDocument(MergeRowTree rowTree, MergeFieldNameTree fieldNameTree, Stream destinationStream)
 {
     using (var writer = XmlWriter.Create(destinationStream)) {
         writer.WriteStartDocument();
         writeRowTreeXmlElement(rowTree, fieldNameTree, writer);
         writer.WriteEndDocument();
     }
 }
        private static void writeRowTreeXmlElement(MergeRowTree rowTree, MergeFieldNameTree fieldNameTree, XmlWriter writer)
        {
            writer.WriteStartElement(rowTree.NodeName);
            foreach (var row in rowTree.Rows)
            {
                writer.WriteStartElement(rowTree.XmlRowElementName);
                foreach (var fieldName in fieldNameTree.FieldNames)
                {
                    var mergeValue = row.Values.SingleOrDefault(i => i.Name == fieldName);
                    if (mergeValue == null)
                    {
                        // Use ApplicationException instead of MailMergingException because the field names can easily be validated before this method is called.
                        throw new ApplicationException("Merge field " + fieldName + " is invalid.");
                    }

                    writer.WriteStartElement(mergeValue.Name);
                    if (mergeValue is MergeValue <string> )
                    {
                        writer.WriteValue((mergeValue as MergeValue <string>).Evaluate(false));
                    }
                    else
                    {
                        // Use ApplicationException instead of MailMergingException because the field names can easily be validated before this method is called.
                        throw new ApplicationException("Merge field " + mergeValue.Name + " evaluates to an unsupported type.");
                    }
                    writer.WriteEndElement();
                }

                foreach (var childNameAndFieldNameTree in fieldNameTree.ChildNamesAndChildren)
                {
                    var childRowTree = row.Children.SingleOrDefault(i => i.NodeName == childNameAndFieldNameTree.Item1);
                    if (childRowTree == null)
                    {
                        // Use ApplicationException instead of MailMergingException because the child names can easily be validated before this method is called.
                        throw new ApplicationException("Child " + childNameAndFieldNameTree.Item1 + " is invalid.");
                    }
                    writeRowTreeXmlElement(childRowTree, childNameAndFieldNameTree.Item2, writer);
                }

                writer.WriteEndElement();
            }
            writer.WriteEndElement();
        }
        private static void writeRowTreeXmlElement( MergeRowTree rowTree, MergeFieldNameTree fieldNameTree, XmlWriter writer )
        {
            writer.WriteStartElement( rowTree.NodeName );
            foreach( var row in rowTree.Rows ) {
                writer.WriteStartElement( rowTree.XmlRowElementName );
                foreach( var fieldName in fieldNameTree.FieldNames ) {
                    var mergeValue = row.Values.SingleOrDefault( i => i.Name == fieldName );
                    if( mergeValue == null ) {
                        // Use ApplicationException instead of MailMergingException because the field names can easily be validated before this method is called.
                        throw new ApplicationException( "Merge field " + fieldName + " is invalid." );
                    }

                    writer.WriteStartElement( mergeValue.Name );
                    if( mergeValue is MergeValue<string> )
                        writer.WriteValue( ( mergeValue as MergeValue<string> ).Evaluate( false ) );
                    else {
                        // Use ApplicationException instead of MailMergingException because the field names can easily be validated before this method is called.
                        throw new ApplicationException( "Merge field " + mergeValue.Name + " evaluates to an unsupported type." );
                    }
                    writer.WriteEndElement();
                }

                foreach( var childNameAndFieldNameTree in fieldNameTree.ChildNamesAndChildren ) {
                    var childRowTree = row.Children.SingleOrDefault( i => i.NodeName == childNameAndFieldNameTree.Item1 );
                    if( childRowTree == null ) {
                        // Use ApplicationException instead of MailMergingException because the child names can easily be validated before this method is called.
                        throw new ApplicationException( "Child " + childNameAndFieldNameTree.Item1 + " is invalid." );
                    }
                    writeRowTreeXmlElement( childRowTree, childNameAndFieldNameTree.Item2, writer );
                }

                writer.WriteEndElement();
            }
            writer.WriteEndElement();
        }
 /// <summary>
 /// Creates an XML document from a row tree and writes it to a stream using UTF-8 encoding.
 /// </summary>
 // If we need to start generating XML on the fly for HTTP responses, it may make sense to create an overload of this method that takes an HttpResponse
 // object instead of a stream. This new overload would use the HttpResponse.Output property to obtain a TextWriter and then create the XmlWriter on top of
 // that instead of a stream. The advantage of this approach is that the encoding of the XML would then be determined by ASP.NET, which takes into account
 // any request headers the client may have sent that pertain to the desired encoding of the response.
 public static void CreateXmlDocument( MergeRowTree rowTree, MergeFieldNameTree fieldNameTree, Stream destinationStream )
 {
     using( var writer = XmlWriter.Create( destinationStream ) ) {
         writer.WriteStartDocument();
         writeRowTreeXmlElement( rowTree, fieldNameTree, writer );
         writer.WriteEndDocument();
     }
 }