Application Security Testing: An Integral Part of DevOps
This first of a multi-part series on using the .NET XML classes begins with the logical first step: programmatically creating and writing XML files or documents. From there, future articles will cover the tasks of sequentially reading XML files, writing a simple XML maintenance application, and using the XML DOM (Document Object Model) classes for a finer level of control over more sophisticated file schemas.
Note: The series assumes that you are familiar with the basics of XML. If you are new to XML, read one of the many fine tutorials on the subject first and then return to this document.
The XmlTextWriter class supports a non-cached, forward-only means of writing W3C-standard XML data to a file. As a result, it is typically used in cases where the application needs to generate an entire XML file from start to finish. If your application needs dynamic access to specific parts of a file, you will need to use the XmlDocument class (which a future article will cover). Also, the XmlTextWriter class performs no validation on the data being written. Therefore, it is up to the programmer to ensure that any data passed to the XmlTextWriter class is well formed, or standard, XML.
Generating an XML File
You take the following four basic steps to generate a properly formed XML file with the XmlTextWriter class:
- Instantiate an XmlTextWriter object.
- Write the XML declaration node.
- Write the data.
- Close the file and perform cleanup.
Instantiate an XmlTextWriter Object
The XmlTextWriter constructor has three distinct versions:
XmlTextWriter(TextWriter* textWriter); XmlTextWriter(Stream* inputStream, Encoding* encoding); XmlTextWriter(String* fileName, Encoding* encoding);
The first two constructors are useful in situations where you already have a stream object associated with a file. The third constructor allows the code to specify the file name. With this constructor, you must ensure that the file exists and handle any file-level exceptions that the XmlTextWriter throws if it encounters problems with the specified file.
Write the XML Declaration Node
You write the XML declaration node with the XmlTextWriter::WriteStartDocument method. It is the first line you see in (well-formed) XML files. This node includes such information as the XML version and encoding method used. Here's an example of that node:
<?xml version="1.0" encoding="UTF-8" ?>
Some files do not include this node, but the W3C standards dictate its inclusion. Therefore, you should create a declaration node if your file will be used by other applications or shared with other users who expect standard XML data.
The WriteStartDocument method has only two overloads:
WriteStartDocument(); WriteStartDocument(bool standAlone);
The first version of this method writes the node with only the version attribute (as seen in the previous example), whereas the second version includes a Boolean value indicating whether you want the standalone attribute explicitly stated and what you want its value to be. Here are a couple of examples of that (where the encoding attribute is based on your locale):
writer->WriteStartDocument(); // produces <?xml version="1.0" encoding="us-ascii"?> writer->WriteStartDocument(true); // produces <?xml version="1.0" encoding="us-ascii" standalone="yes"?> writer->WriteStartDocument(false); // produces <?xml version="1.0" encoding="us-ascii" standalone="no"?>
Write the Data
Once you've written the declaration node, you can write the nodes and attributes that constitute your file's data. There are about a dozen XmlTextWriter methods, so I won't delve into the details of all of them. Rather, I discuss the higher-level process of writing nodes and attributes and give the following examples:
- Starting and ending an elementKeeping in mind that the XmlTextWriter is a forward-only writer, you have to write each part of the file as that file will appear. Therefore, if you want to output an element containing an element, an attribute, and a value, you must make the appropriate calls in that order. To begin that, call the XmlTextWriter::WriteStartElement method. From there, make calls to methods to insert attributes, values, and other elements. Indicate that the element is to be closed via a call to XmlTextWriter::WriteEndElement:
writer->WriteStartElement(S"Article"); // write xml to appear within this element writer->WriteEndElement();
- Writing attributesWithin a WriteStartElement/WriteEndElement pair, you can write a given element's attributes. This is done via the XmlTextWriter::WriteAttributeString method. The following snippet writes an attribute called "lang" with a value of "EN" to the element currently being written:
- Writing an element's valueThere are several different specialized methods for writing the value of the current element. Of these, the most commonly used is the XmlTextWriter::WriteString method, which writes the specified value to the current element:
writer->WriteString(S"Writing XML Files using the XmlTextWriterClass");
- Writing CDATAIf an XML parser will read the data you write, you have to be careful not to break any rules regarding what that data can include. Otherwise, it will confuse the parser. In such cases, specify that the element type is CDATA and write it using the XmlTextWriter::WriteCData method:
- Writing a simple element without attributes or nested elementsIf you need to write an element containing a value and that element doesn't have any other information associated with it (such as attributes or other nested elements), you can use the XmlTextWriter::WriteElementString method:
writer->WriteElementString(S"Author", S"Tom Archer");