Customized XML Writer Creation

The XmlTextWriter implementation does not do the following:

  • The XmlTextWriter does not verify that element or attribute names are valid.
  • The XmlTextWriter writes Unicode characters in the range 0x0 to 0x20, and the characters 0xFFFE and 0xFFFF, which are not XML characters.
  • The XmlTextWriter does not detect duplicate attributes. It will write duplicate attributes without throwing an exception.

If these three items are critical to your needs, then you can write a custom writer that extends the current XmlTextWriter and add this functionality.

The following example shows how you could modify the XML writer, overwriting the WriteString and WriteStartElement methods. The code sample does not guarantee that valid XML is written. It only checks that strings written out do not have characters that are out of bounds, and that element names are valid. It does not check attribute names.

  1. The first step is to define a method that verifies that the characters in a string are valid characters, according to the W3C specification. This method is used to verify that the strings written out by the writer are correct. The specific tests done on the character strings are:

    • Verify that no character has a hex value greater than 0xFFFD, or less than 0x20.
    • Check that the character is not equal to the tab (\t), the newline (\n), the carriage return (\r), or is an invalid XML character below the range of 0x20. If any of these characters occur, an exception is thrown.

    The following code example shows this method.

    internal void CheckUnicodeString(String value) 
        {
        for (int i=0; i < value.Length; ++i) {
            if (value[i] > 0xFFFD) 
            {
                throw new Exception("Invalid Unicode");
            } 
            else if (value[i] < 0x20 && value[i] != '\t' & value[i] != '\n' & value[i] != '\r')
            {
                throw new Exception("Invalid Xml Characters");
            }
        }
    
  2. The next step is to use the method defined in step 1 to overwrite the WriteString method in the XmlTextWriter. The overwritten WriteString method throws an exception if the characters in the string are out of the bounds of the acceptable character range.

            public override void WriteString(String value) 
             {
                CheckUnicodeString(value);
                base.WriteString(value);
             }
    

To verify that the names written by the WriteStartElement method are valid, the EncodeLocalName method of the XmlConvert class is used. The EncodeLocalName method ensures that the characters in the name are valid by converting any invalid character to a valid representation. For example, Order Details would be converted to Order_x0020_Details. For more information on the EncodeLocalName, see XmlConvert.EncodeLocalName Method.

public override void WriteStartElement(string prefix, string localName, string ns) 
{
   base.WriteStartElement(prefix,
        XmlConvert.EncodeLocalName(localName),ns);
}

Instead of using the EncodeLocalName method, the VerifyName method of the XmlConvert class also verifies that an element or attribute name is valid. The VerifyName method returns the name as the return argument if the name is valid, or throws an exception if the name is not valid. This is useful if you want to know that an error occurred due to an invalid name. The following example shows how to create the WriteStartElement method using the VerifyName method to check if the name is valid.

public override void WriteStartElement(string prefix, string localName, 
                                       string ns) 
{
   base.WriteStartElement(prefix,
        XmlConvert.VerifyName(localName),ns);
}

Note   None of the other WriteStartElement methods need to be overwritten since they all call the above method.

Conformant Writer Full Code Sample

The following code sample is a full listing of the class definition. In order to ensure that valid characters are always written out, additional methods need to be overridden, such as the WriteDocType, WriteAttributeString, WriteElementString, and WriteCharEntity methods. Overriding these methods is similar to the code shown to override the WriteStartElement method.

Imports System
Imports System.IO
Imports System.Text
Imports System.Xml
Imports System.Collections
Imports Microsoft.VisualBasic

'------------------------------------------------------------------
' Class derived from XmlTextWriter classs.
'
' This example only overrides the WriteString and WriteStartElement
' methods.
'------------------------------------------------------------------
namespace MyWriter.ConformWriter 

    public class ConformWriter : Inherits XmlTextWriter

        private sub CheckUnicodeString(ByVal value as String) 
            Dim i as Integer
            Dim iVal as Integer
            for i=0 to value.Length-1
                iVal = Convert.ToInt16(value.Chars(i))
                if (iVal > &HFFFD) 
                    throw new Exception("Invalid Unicode")
                else 
                    if iVal < &H20 And (iVal <> 9 And iVal <> 13 And iVal <> 10) Then
                        throw new Exception("Invalid Xml Characters")
                    end if
                end if
            next
        end sub

        public sub New(ByVal fileName as String, ByVal encoding as Encoding)
            MyBase.New(fileName, encoding) 
        end sub

        Overrides public sub WriteString(ByVal value as String) 
            CheckUnicodeString(value)
            MyBase.WriteString(value)
        end sub

        Overrides Overloads public sub WriteStartElement(ByVal prefix As String, ByVal localname As String, ByVal ns As String) 
            MyBase.WriteStartElement(prefix, XmlConvert.EncodeLocalName(localName), ns)
        end sub
        
    end class
end namespace
[C#]
using System;
using System.IO;
using System.Text;
using System.Xml;
using System.Collections;

//------------------------------------------------------------------
// Class derived from XmlTextWriter classs.
//
// This sample only overrides the WriteString and WriteStartElement
// methods.
//------------------------------------------------------------------
namespace MyWriter.ConformWriter {

    public class ConformWriter : XmlTextWriter
    {
       internal void CheckUnicodeString(String value) {
           for (int i=0; i < value.Length; ++i) {
                if (value[i] > 0xFFFD) {
                    throw new Exception("Invalid Unicode");
                        } else if (value[i] < 0x20 && value[i] != '\t' & value[i] != '\n' & value[i] != '\r') {
                    throw new Exception("Invalid Xml Characters");
                }
            }
        }

        public ConformWriter(String fileName, Encoding encoding):base(fileName, encoding) {}

        public override void WriteString(String value) {
            CheckUnicodeString(value);
            base.WriteString(value);
        }

        public override void WriteStartElement(string prefix, string localName, string ns) {
            base.WriteStartElement(prefix, XmlConvert.EncodeLocalName(localName), ns);
        }
    }
}

See Also

Writing XML with the XmlWriter