[ Team LiB ] |
3.2 XmlWriter and Its SubclassesXmlWriter is an abstract base class that defines the interface for creating XML output programmatically. It contains methods such as WriteStartElement( ) and WriteEndElement( ) to write data. XmlWriter maintains the state of the XML document as it writes, so it knows which start element or attribute to close when you call WriteEndElement( ) or WriteEndAttribute( ). XmlTextWriter is the subclass of XmlWriter, which provides support for output of XML to any Stream, filename, or TextWriter. In addition to all the required features of an XmlWriter, XmlTextWriter allows you to set the formatting of the output, using the Formatting, Indentation, IndentChar, Namespaces, and QuoteChar properties. The XmlTextWriter formatting properties are described in Table 3-6.
With an XmlTextWriter, you always have access to the base Stream through the BaseStream property. This is useful for manipulating the Stream at runtime, such as calling Seek( ) to reset the Stream's position. 3.2.1 When to Use XmlWriterYou should use XmlWriter when you want to output data from your program to a file or Stream in XML format. Since XmlTextWriter's constructors take a Stream, a filename, and a TextWriter, respectively, you can output to any common I/O target. In addition, any .NET class that takes an XmlWriter to output its data as XML may use any XmlWriter subclass. This means, for example, that you may output a DOM tree in any of the supported formats. You may also send data to any other format, as long as an XmlWriter exists to produce that format. Like XmlReader, you can extend XmlWriter to produce output in alternative XML syntaxes—or even syntaxes that have nothing whatsoever to do with XML, as long as the XmlWriter interface is supported. 3.2.2 Using the XmlWriterXmlWriter contains methods to write any type of XML node to its output Stream. There is just one subclass in the .NET Framework, XmlTextWriter, which will handle most of your XML output needs. Example 3-3 shows a short program that writes data to an XmlTextWriter. Example 3-3. Program to write XML with XmlTextWriterusing System; using System.IO; using System.Text; using System.Xml; public class WriteXml { public static void Main(string [ ] args) { // Create the XmlWriter XmlTextWriter writer = new XmlTextWriter(Console.Out); // Set the formatting to something nice writer.Formatting = Formatting.Indented; // Write the XML declaration writer.WriteStartDocument(true); // Write a comment writer.WriteComment("the first element follows."); // Start the root element writer.WriteStartElement("root"); // Write an attribute writer.WriteAttributeString("id","_1"); // Write another attribute writer.WriteStartAttribute"name", "foo"); writer.WriteString("bar"); writer.WriteEndAttribute( ); // Write another element writer.WriteElementString("element1","some characters"); writer.WriteStartElement("cdataElement"); writer.WriteAttributeString("date",DateTime.Now.ToString( )); writer.WriteCData("<this contains some characters XML wouldn't like & would choke on"); writer.WriteString("<this contains some characters XML wouldn't like & so the XmlWriter replaces them"); writer.WriteEndElement( ); // Write an empty element writer.WriteStartElement("emptyElement"); writer.WriteEndElement( ); // Write another empty element writer.WriteStartElement("emptyElement","foo"); writer.WriteFullEndElement( ); // Write some text writer.WriteString("One string "); writer.WriteEntityRef("amp"); writer.WriteString(" another."); // Close the root element writer.WriteEndElement( ); // End the document writer.WriteEndDocument( ); // Flush and close the output stream writer.Flush( ); writer.Close( ); } } Let's walk through this code in small chunks: XmlTextWriter writer = new XmlTextWriter(Console.Out); Console.Out is just like any other TextWriter, so you can pass it to the XmlTextWriter constructor. If you wanted, you could have instead created a new StreamWriter and written the XML to a file or an HttpWebRequest: writer.Formatting = Formatting.Indented; This produces XML formatted with indenting, as described previously. It's easier to read, but doesn't affect the code at all: writer.WriteStartDocument(true); This line writes the XML declaration. There are two overloads of WriteStartDocument( ); Example 3-3 uses the one that takes a bool parameter, indicating that the XML declaration should include standalone="yes". XmlWriter will determine the correct encoding from the underlying TextWriter. If I had chosen to write to a file, the encoding would be UTF8:
writer.WriteComment("the first element follows."); This line writes an XML comment to the Stream. Although XML comments are enclosed in angle brackets (<!-- ... -->), all you have to pass to WriteComment( ) is the text of the comment: writer.WriteStartElement("root"); Now I begin to write the first element. The WriteStartElement( ) method writes the opening angle bracket and the element name (<root) to the Stream; XmlWriter is smart enough to wait for any attributes before writing the closing angle bracket. There are several overloads of WriteStartElement( ), providing the flexibility to specify a namespace and prefix. Here I'm just writing a plain element name: writer.WriteAttributeString("id","_1"); Before moving on to the next element, this line adds an attribute to the root element. Like WriteStartElement( ), WriteAttributeString( ) has several overloads, allowing you to specify the namespace and prefix, as well as the local name and value: writer.WriteStartAttribute("name", "foo"); writer.WriteString("bar"); writer.WriteEndAttribute( ); These lines use another method, WriteStartAttribute( ), that has similar overloads. In this case, I have indicated the start of an attribute named name, whose namespace is foo. Because I did not specify a prefix, XmlWriter will automatically assign a prefix for the namespace. If I had previously used the same namespace in a different element or attribute, XmlWriter is smart enough to use the same prefix, whether I had assigned the prefix myself or had one assigned for me by XmlWriter. After starting to write the attribute, I've used WriteString( ) to write the attribute's value, "bar", and finally closed the attribute with WriteEndAttribute( ): writer.WriteElementString("element1","some characters"); WriteElementString( ), like WriteAttributeString( ), writes the entire element with the specified text content. It also includes the closing angle bracket, so there is no need to call WriteEndElement( ): writer.WriteStartElement("cdataElement"); writer.WriteAttributeString("date",DateTime.Now.ToString( )); writer.WriteCData("<this contains some characters XML wouldn't like & would choke on"); writer.WriteString("<this contains some characters XML wouldn't like & so the XmlWriter replaces them"); writer.WriteEndElement( ); This step writes an element named cdataElement. This element has an attribute called date, but, more importantly, its content begins with a CDATA section. XmlWriter takes care of adding the <![CDATA[...]]> markup around the character data. The call to WriteString( ) writes a similar string; in this case, XmlWriter will replace the special characters with the appropriate entities: writer.WriteStartElement("emptyElement"); writer.WriteEndElement( ); writer.WriteStartElement("emptyElement","foo"); writer.WriteFullEndElement( ); These four lines show two different ways to write empty elements. The first one will be written as <emptyElement/> because XmlWriter knows that the element is empty. In the second one, even though the element is empty, XmlWriter will write a full end element (<emptyElement></emptyElement>) because WriteFullEndElement( ) was specifically called. In addition, in the second empty element, I specified the namespace foo. Because it is the same namespace used in an attribute earlier, XmlWriter automatically uses the same prefix, which it originally assigned when writing that attribute: writer.WriteString("One string "); writer.WriteEntityRef("amp"); writer.WriteString(" another."); These lines write some text to the XML. WriteString( ) writes the text verbatim, while WriteEntityRef( ) writes an entity reference. WriteEntityRef( ) takes care of formatting the entity reference correctly; in this case & will be written to the Stream: writer.WriteEndElement( ); writer.WriteEndDocument( ); The final call to WriteEndElement( ) closes the root element, because XmlWriter keeps track of its depth in the node tree. WriteEndDocument( ) closes the document: writer.Flush( ); writer.Close( ); Finally, you should always remember to flush and close the Stream. Compiling and running this program, the following output is written to the console: <?xml version="1.0" encoding="IBM437" standalone="yes"?> <!--the first element follows.--> <root id="_1" d1p1:name="bar" xmlns:d1p1="foo"> <element1>some characters</element1> <cdataElement date="7/1/2002 9:26:22 PM"><![CDATA[<this contains some characters XML wouldn't like & would choke on]]><this contains some characters XML wouldn't like & so the XmlWriter replaces them</cdataElement> <emptyElement /> <d1p1:emptyElement> </d1p1:emptyElement>One string & another.</root> Remember the root element, with its name="bar" attribute? In the output, you can see that XmlWriter selected the prefix d1p1 for the namespace foo. It also remembered this prefix and used it for the emptyElement element later in the output. Remember that I could have assigned the prefix myself, with a different overload of WriteStartAttribute( ): writer.WriteStartAttribute("baz", "name", "foo"); The output would then have changed to this: <?xml version="1.0" encoding="IBM437" standalone="yes"?> <!--the first element follows.--> <root id="_1" baz:name="bar" xmlns:baz="foo"> <element1>some characters</element1> <cdataElement date="7/1/2002 9:26:22 PM"><![CDATA[<this contains some characters XML wouldn't like & would choke on]]><this contains some characters XML wouldn't like & so the XmlWriter replaces them</cdataElement> <emptyElement /> <baz:emptyElement> </baz:emptyElement>One string & another.</root> Notice that subsequent calls to WriteStartElement( ) with the same name still properly pick up the prefix, whether you set it or XmlWriter sets it. |
[ Team LiB ] |