Well-Formed XML Creation with the XmlTextWriter

The XmlTextWriter, derived from the XmlWriter, writes XML to a file, console, stream, and other output types. When writing XML, the methods do extra work to produce well-formed XML. The following table provides a list of methods that do work for you to ensure that data is well-formed.

Method Description of work done
WriteAttributeString XmlTextWriter escapes the text content of the attribute depending on what it finds.
WriteString XmlTextWriter escapes special characters, replacing them with & < > and numeric character entities when required.
WriteBase64 XmlTextWriter encodes the base64 bytes, which can then be read using ReadBinary on the XmlReader.
Close Close checks to see if the XML is an invalid XML document, and if so, an InvalidOperationException is thrown.

The following additional tasks are done by the XmlTextWriter to ensure well-formed XML:

  • Ensures that the XML elements are written out in the correct order. For example, it will not let you write an attribute outside of an element, write a CDATA block inside an attribute, or write multiple root elements. In addition, it ensures that the <?xml declaration comes first and that the <!DOCTYPE node comes before the root element.

  • Ensures that value and format of the xml:space attribute is correct and makes sure that its value is acceptable according to the Extensible Markup Language (XML) 1.0 (Second Edition) recommendation (www.w3.org/XML/Group/2000/07/REC-xml-2e-review\#sec-white-space). The following example shows the use of a valid value for xml:space in the WriteAttributeString method:

    w.WriteAttributeString("xml:space", "", "preserve");
    

    Valid values xml:space are "default" and "preserve". If the argument is not one of these values, an ArgumentException is thrown.

  • Checks when a string is used as a parameter, for example Null==String.Empty and String.Empty and whether it follows the W3C rules.

The following table shows the additional methods and properties that are defined by the XmlTextWriter that are not inherited or defined in XmlWriter, or inherited from Object.

Method or Property Description
XmlTextWriter Constructor Creates an instance of the XmlTextWriter, which takes a filename, stream, or TextWriter. An overloaded method exists to take an additional parameter that defines the encoding type.
Namespaces Property Specifies whether namespaces are supported. When this is set to false, xmlns declarations are not written, and you can specify element names containing any number of colons.
Formatting Property Defines whether indenting is used to format the output.
IndentChar Property Defines which character to use for indenting, when doing indented Formatting.
Indentation Property Defines how many IndentChars to write for each level in the hierarchy when doing indented Formatting.
QuoteChar Property Defines which character to use to quote attribute values. This must be a single quote &#39; or a double quote &#34;.
BaseStream Returns the Stream that the XmlTextWriter is writing to. Returns null if the XmlTextWriter was constructed with a TextWriter that is not derived from StreamWriter.

The following example creates XML output using the XmlTextWriter.

Shared Sub WriteQuote(writer As XmlWriter, symbol As String, price As Double, change As Double, volume As Long)
   writer.WriteStartElement("Stock")
   writer.WriteAttributeString("Symbol", symbol)
   writer.WriteElementString("Price", XmlConvert.ToString(price))
   writer.WriteElementString("Change", XmlConvert.ToString(change))
   writer.WriteElementString("Volume", XmlConvert.ToString(volume))
   writer.WriteEndElement()
End Sub 'WriteQuote

Public Shared Sub Main()
   Dim writer As New XmlTextWriter(Console.Out)
   writer.Formatting = Formatting.Indented
   WriteQuote(writer, "MSFT", 74.125, 5.89, 69020000)
   writer.Close()
End Sub 'Main
[C#]
static void WriteQuote(XmlWriter writer, string symbol, 
                double price, double change, long volume)
{
   writer.WriteStartElement("Stock");
   writer.WriteAttributeString("Symbol", symbol);
   writer.WriteElementString("Price", XmlConvert.ToString(price));
   writer.WriteElementString("Change", XmlConvert.ToString(change));
   writer.WriteElementString("Volume", XmlConvert.ToString(volume));
   writer.WriteEndElement();
}

public static void Main(){
    XmlTextWriter writer = new XmlTextWriter(Console.Out);
    writer.Formatting = Formatting.Indented;
    WriteQuote(writer, "MSFT", 74.125, 5.89, 69020000);
    writer.Close();
}

Output

<Stock Symbol="MSFT">
      <Price>74.125</Price>
      <Change>5.89</Change>
      <Volume>69020000</Volume>
</Stock>

The input to the WriteQuote method is the stock symbol, which comes in as a string. The price and change are declared as double and the volume is long. To convert these variables to strings, the XmlConvert class is used. It has methods that convert all strong-data types to strings. In addition, the XmlConvert class has methods that do the opposite conversion by converting strings to .NET Framework data types. For more information, see Character Encoding of XML Names and Conversion of XML Data Types.

For sample code that demonstrates writing XML to a file, see XmlTextWriter.WriteProcessingInstruction. For sample code that demonstrates writing XML to the console, see XmlTextWriter.WriteString.

The following code shows how to write an element that produces <price>19.95</price>:

'Write the price.
writer.WriteElementString("price", "19.95")
[C#]
//Write the price.
writer.WriteElementString("price", "19.95");

The following code shows how to write an attribute that produces <element name="purchaseOrder"/>:

writer.WriteStartElement("element")
writer.WriteAttributeString("name", "purchaseOrder")
writer.WriteEndElement()
[C#]
writer.WriteStartElement("element"); 
writer.WriteAttributeString("name", "purchaseOrder"); 
writer.WriteEndElement();

WriteAttributeString Method Writes Attributes and Namespace Declarations

The WriteAttributeString method has two different tasks. One task is to write out attributes and associate them with a user defined namespace prefix. The second task is to generate namespace declarations. If writing attributes and the localname parameter is xmlns, then this method is considered to be creating a namespace declaration.

In the following code example, the WriteAttributeString method is used to write attributes inside an element.

'Write the genre attribute.
writer.WriteAttributeString("genre", "novel")
'Write the ISBN attribute.
writer.WriteAttributeString("ISBN", "1-8630-014")
[C#]
//Write the genre attribute.
writer.WriteAttributeString("genre", "novel");
//Write the ISBN attribute.
writer.WriteAttributeString("ISBN", "1-8630-014");

The WriteAttributeString also escapes the text content of the attribute depending on what it finds. If double quotes are used, the XmlTextWriter escapes them in the text content of the attribute value with &quot;. If single quotes are used, it escapes the text content of the attribute value with &apos;.

To generate namespace declarations, there is an overloaded WriteAttributeString method that allows the application to define a namespace declaration. The following code example creates two default namespaces. The first declaration binds all elements with no prefix to the first namespace declaration, while any element declared with a "po" prefix is bound to the second namespace declaration.

' Write the default namespace, identified as xmlns with no prefix
writer.WriteAttributeString("xmlns", Nothing, "http://www.w3.org/2000/10/XMLSchema")
' Write a namespace for the purchase order with a prefix of "po"
writer.WriteAttributeString("xmlns", "po", Nothing, "https://contoso.com/po")
[C#]
// Write the default namespace, identified as xmlns with no prefix
writer.WriteAttributeString("xmlns", null, "http://www.w3.org/2000/10/XMLSchema");
// Write a namespace for the purchase order with a prefix of "po"
writer.WriteAttributeString("xmlns", "po", null, "https://contoso.com/po");

Close Method

The Close method checks that the XML document is valid when the stream is closed. This prevents invalid XML documents from being created, and ensures that the XML is well-formed. In addition to closing the stream, the Close method also calls all necessary WriteEnd<xxx> methods to close the document.

Method Pairs

Methods in the XmlWriter also come in pairs; the WriteStartDocument and WriteEndDocument, WriteStartElement and WriteEndElement, and the WriteStartAttribute and WriteEndAttribute method pair. For example, using these methods you can create nested elements or attributes. It is with these method pairs that an XML document is built and allows the creation of complex elements or attributes.

WriteStartDocument and WriteEndDocument Method

The WriteStartDocument starts a new document and writes the XML declaration with version attribute set to "1.0", and the WriteEndDocument closes that document. Between calling the next WriteStartDocument to start writing the next document, the formatting, indentation, and other properties can be modified. The WriteStartDocument method programmatically recognizes an XML document is being written and applies root-level rules. If this method is not used an XML fragment is created and it verifies whether it is well-formed. Root level rules are not applied. The following code example shows the start and end in a document.

' Write the XML declaration.
writer.WriteStartDocument()
. . .
' Close the document.
writer.WriteEndDocument()

[C#]
// Write the XML declaration. 
writer.WriteStartDocument();
. . .
// Close the document.
writer.WriteEndDocument();

WriteStartElement and WriteEndElement Method

The WriteStartElement and WriteEndElement pair delimits one or more elements. In all the overridden WriteStartElement methods, a local name for the start tag is a required parameter. The following code uses the WriteStartElement and WriteEndElement method pair.

' Write the title.
writer.WriteStartElement("title")
writer.WriteString("The Handmaid's Tale")
writer.WriteEndElement()

[C#]
// Write the title.
writer.WriteStartElement("title");
writer.WriteString("The Handmaid's Tale");
writer.WriteEndElement();

Output

<title>The Handmaid's Tale</title>

The WriteStartElement provides an overridden method signature that enables the code to specify namespace prefixes for its elements. For more information, see Element Namespace Prefixes in the XmlTextWriter.

WriteStartAttribute and WriteEndAttribute Method

The WriteStartAttribute and WriteEndAttribute are similar to other start and end methods, except these methods start and end attributes. The WriteStartAttribute writes the start of the attribute, a WriteString method is used to write the attribute value, and the WriteEndAttribute ends the attribute tag. The following code example shows the WriteStartAttribute and WriteEndAttribute method pair.

writer.WriteStartAttribute(prefix, "ISBN", "urn:samples")
writer.WriteString("1-861003-78")
writer.WriteEndAttribute()

[C#]
writer.WriteStartAttribute(prefix, "ISBN", "urn:samples");
writer.WriteString("1-861003-78");
writer.WriteEndAttribute();

Output

<book bk:ISBN="1-861003-78">

The WriteStartAttribute has an overloaded method that enables an application to specify a namespace prefix so that it can associate the namespace prefix with the attributes that it writes. For more information, see Attribute Namespace Prefixes in the XmlTextWriter.

See Also

Writing XML with the XmlWriter | XML Output Formatting with XmlTextWriter | Namespace Features within the XmlTextWriter | Customized XML Writer Creation | XmlTextWriter Class | XmlTextWriter Members | XmlWriter Class | XmlWriter Members