C# XmlWriter

Extensible markup language (XML)

XmlWriter writes XML data from objects in memory. XML files are excellent for interoperability with other systems and Internet sites. We use XmlWriter in a C# program. We review each step required. We note possible problems with this type.

This C# tutorial demonstrates the XmlWriter type. It shows how you can use XmlWriter with objects.

Example

Note

First, we see the finished code from this tutorial. You can see that there is an Employee class, which contains four fields, four public getters, and a constructor. The class is properly encapsulated and has exclusive control over its internal fields.

Program that uses XmlWriter [C#]

using System.Xml;

class Program
{
    class Employee
    {
	int _id;
	string _firstName;
	string _lastName;
	int _salary;

	public Employee(int id, string firstName, string lastName, int salary)
	{
	    this._id = id;
	    this._firstName = firstName;
	    this._lastName = lastName;
	    this._salary = salary;
	}

	public int Id { get { return _id; } }
	public string FirstName { get { return _firstName; } }
	public string LastName { get { return _lastName; } }
	public int Salary { get { return _salary; } }
    }

    static void Main()
    {
	Employee[] employees = new Employee[4];
	employees[0] = new Employee(1, "David", "Smith", 10000);
	employees[1] = new Employee(3, "Mark", "Drinkwater", 30000);
	employees[2] = new Employee(4, "Norah", "Miller", 20000);
	employees[3] = new Employee(12, "Cecil", "Walker", 120000);

	using (XmlWriter writer = XmlWriter.Create("employees.xml"))
	{
	    writer.WriteStartDocument();
	    writer.WriteStartElement("Employees");

	    foreach (Employee employee in employees)
	    {
		writer.WriteStartElement("Employee");

		writer.WriteElementString("ID", employee.Id.ToString());
		writer.WriteElementString("FirstName", employee.FirstName);
		writer.WriteElementString("LastName", employee.LastName);
		writer.WriteElementString("Salary", employee.Salary.ToString());

		writer.WriteEndElement();
	    }

	    writer.WriteEndElement();
	    writer.WriteEndDocument();
	}
    }
}
Main method

Main method. The Main entry point here first instantiates an array of four Employee instances. The constructor from the Employee class is used, and four employee items are assigned to the array slots, specifying the ID, first name, last name, and salary figure.

Main Args Examples

XmlWriter. The XmlWriter code, which begins right after the array is assigned, is explained thoroughly in the following sections. If you have only line wrong or incorrect, the program will fail at runtime.

Create new XmlWriter

This is how we can start the XmlWriter code that is used in the Main method above. To create an XmlWriter, you must assign a variable to the result of the XmlWriter.Create method. Here, the XmlWriter.Create method is told to create a new file called employees.xml. This is where we will store the array data.

Fragment that calls XmlWriter.Create [C#]

using (XmlWriter writer = XmlWriter.Create("employees.xml"))
{
    writer.WriteStartDocument();
    writer.WriteEndDocument();
}

Keyword using

Warning

I would like to emphasize that the 'using' statement here, which wraps all the XmlWriter code, is important to remember. What the using statement does is ensure that the system resources are always released. If you manually call Dispose, you will lose system resources when exceptions are thrown. The 'using' keyword here basically is a shorthand for the 'finally' statement.

Using Statement Calls Dispose Finally

Write root element

Next here we need to add a root element to the XML. It is very important to add a root element with XmlWriter that contains all the other elements. There can be only one root element and it can have any name. If you don't create the root element, you get a perplexing exception. Please see the exception below.

Fragment that calls WriteStartElement [C#]

using (XmlWriter writer = XmlWriter.Create("employees.xml"))
{
    writer.WriteStartDocument();
    writer.WriteStartElement("Employees"); // <-- Important root element
    writer.WriteEndElement();              // <-- Closes it
    writer.WriteEndDocument();
}

Write array elements

Next, we can loop through the elements in our Employee array. The array we use here is shown in full in the first block of code above. The foreach loop is used because we don't need any special indexing. The WriteStartElement method begins a new block, which is empty.

Foreach Loop Examples
Fragment that uses foreach [C#]

using (XmlWriter writer = XmlWriter.Create("employees.xml"))
{
    writer.WriteStartDocument();
    writer.WriteStartElement("Employees");

    foreach (Employee employee in employees)  // <-- This is new
    {
	writer.WriteStartElement("Employee"); // <-- Write employee element
	writer.WriteEndElement();
    }

    writer.WriteEndElement();
    writer.WriteEndDocument();
}

Write object fields

Here we take the values from each Employee object in the array and access the property getters. These getters complicate the code, but provide a much safer system where data integrity is more likely to be preserved. The four properties from the Employee object are inserted into special tags.

Fragment that calls WriteElementString [C#]

using (XmlWriter writer = XmlWriter.Create("employees.xml"))
{
    writer.WriteStartDocument();
    writer.WriteStartElement("Employees");

    foreach (Employee employee in employees)
    {
	writer.WriteStartElement("Employee");

	writer.WriteElementString("ID", employee.Id.ToString());   // <-- These are new
	writer.WriteElementString("FirstName", employee.FirstName);
	writer.WriteElementString("LastName", employee.LastName);
	writer.WriteElementString("Salary", employee.Salary.ToString());

	writer.WriteEndElement();
    }

    writer.WriteEndElement();
    writer.WriteEndDocument();
}

Root element exception

When attempting to use XmlWriter over the years, I have often encountered this InvalidOperationException. The problem here is that you have to put the root element into your document always.

System.InvalidOperationException: Token StartElement in state EndRoot Element would result in an invalid XML document. Make sure that the ConformanceLevel setting is set to ConformanceLevel.Fragment or ConformanceLevel.Auto if you want to write an XML fragment.

More options

We have barely scratched the surface of XmlWriter so far. For completeness, my notes on the class are presented in the following table.

XmlWriterSettings This is an object that you can pass to the XmlWriter.Create method to specify various options of how XmlWriter operates. Its constructor does not accept any parameters.

XmlWriter.Create This method is shown in this article. This is a static method that returns a new instance of an XmlWriter. It is important that you wrap this in a using statement so the system resources are cleaned up afterwards.

CheckedCharacters This performs character-level validation of the XML you are writing. This can be useful in situations where invalid XML would be disastrous.

IndentChars NewLine These options let you specify how the resulting XML is formatted. They are fairly straightforward and described on MSDN.

Output

Here we see the XML that the program described in this tutorial will generate. You can see that there are four employee elements with four sub-elements in each. It does not have attributes on the elements, but is a properly formed document. The screenshot shows XML Notepad, which is available from Microsoft freely.

Contents of output file [XML]

<?xml version="1.0" encoding="utf-8"?>
<Employees>
    <Employee>
	<ID>1</ID>
	<FirstName>David</FirstName>
	<LastName>Smith</LastName>
	<Salary>10000</Salary>
    </Employee>
    <Employee>
	<ID>3</ID>
	<FirstName>Mark</FirstName>
	<LastName>Drinkwater</LastName>
	<Salary>30000</Salary>
    </Employee>
    <Employee>
	<ID>4</ID>
	<FirstName>Norah</FirstName>
	<LastName>Miller</LastName>
	<Salary>20000</Salary>
    </Employee>
    <Employee>
	<ID>12</ID>
	<FirstName>Cecil</FirstName>
	<LastName>Walker</LastName>
	<Salary>120000</Salary>
    </Employee>
</Employees>
XML results shown in XML Notepad application

Dispose

Question and answer

Is it necessary to call the Close method on an XmlWriter? If you use the using-statement on an XmlWriter, the Dispose method will be called. In the Dispose method, we see that the Close method is called. So you do not need to call Close if you use using(XmlWriter... ). Otherwise, you should call Close explicitly.

Dispose method implementation [IL]

.method family hidebysig newslot virtual
	instance void  Dispose(bool disposing) cil managed
{
  // Code size       19 (0x13)
  .maxstack  8
  ...
  IL_000c:  ldarg.0
  IL_000d:  callvirt   instance void System.Xml.XmlWriter::Close()
  IL_0012:  ret
} // end of method XmlWriter::Dispose

Read XML

This article is focused on writing XML, not reading it. However, the two procedures have a lot in common, and a companion article is available at this site. The XmlReader article has a slightly different focus—it is ordered in a clearer way.

XmlReader Program

Summary

The C# programming language

We saw a tutorial of using XmlWriter in the C# language, covering how you can use the using statement; the XmlWriter.Create method; the WriteStartDocument and WriteEndDocument methods; the WriteStartElement and WriteEndElement methods; and the WriteElementString method. We also looked at some common pitfalls, and checked XML Notepad and the output XML.

XML Tutorials
.NET