HomeSearch

C# XmlWriter, Create XML File

Use the XmlWriter type with objects to write XML data to a file.
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.XmlReader
Example. 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. All the fields are private, not public.Public, private

Also: The class is properly encapsulated and has exclusive control over its internal fields.

Class

Main: We create an array of 4 Employee instances. The constructor from the Employee class is used, and employee items are assigned to the array slots.

Array

New: 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.

C# program that uses XmlWriter 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(); } } }
Notes, using. 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.Using
Write root. Next, we need to add a root element to the XML. It is 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.

Info: If you don't create the root element, you get a perplexing exception.

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. 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.

Note: The WriteStartElement method begins a new block, which is empty. It contains no data.

Foreach
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. 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.

Here: The 4 fields from the Employee object are inserted into special tags in the XML.

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(); }
Problem. When attempting to use XmlWriter over the years, I have often encountered this InvalidOperationException. The problem here is that you always have to put the root element into your document.
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.
Options. We have barely scratched the surface of XmlWriter so far. For completeness, I present my notes on the class. There are many more options that I do not describe—many classes are complex in the .NET Framework.

XmlWriterSettings: This is an object that you can pass to the XmlWriter.Create method to specify various options of how XmlWriter operates.

XmlWriter.Create: This is a static method that returns a new XmlWriter. It is important that you wrap this in a using-statement.

CheckedCharacters: This performs character-level validation of the XML you are writing. This can ensure correctness.

IndentChars, NewLine: These options let you specify how the resulting XML is formatted. They are straightforward to use.

Output. We 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 XML could be used as a data file for an application.

Note: The screenshot shows XML Notepad, which is a program available from Microsoft.

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>
Dispose. 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. 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
Summary. This tutorial used XmlWriter. It covered how you can use the using-statement. And it invoked several methods on the type. We looked at some common pitfalls, and checked XML Notepad and the output XML.
© 2007-2019 Sam Allen. Every person is special and unique. Send bug reports to info@dotnetperls.com.
Home
Dot Net Perls