i-think Twenty-Two

Now with more coherency.

Writing XML with XElement

| Comments

In my last post we looked at how you can use LINQ to XML and XElement to parse XML. But what if you want to create XML files programmatically? Or modify an existing XML document?

Let’s start by looking at how we might add a new entry to our blog. Here is the XML file again:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
<?xml version="1.0" encoding="UTF-8"?>
<Blog>
   <Entries>
      <Entry Archived="false">
         <Title>My First Post</Title>
         <Body>I love LINQ. It's the best</Body>
         <Comments>
            <!-- TODO: Shouldn't comments have authors? -->
            <Comment>I love LINQ more</Comment>
            <Comment>LINQ is the way of the future.</Comment>
         </Comments>
      </Entry>
   </Entries>
</Blog>

So we want to add a new Entry under the Entries element. We’ll also assume that our XML file has been parsed into an XElement variable blog.

We’ll start by creating our entry first:

1
2
3
4
5
var entry = new XElement("Entry");
entry.SetAttributeValue("Archived", false);
entry.Add(new XElement("Title", "My Second Post"));
entry.Add(new XElement("Body", "Just a quick post."));
entry.Add(new XElement("Comments"));

We started by creating the element, set the “Archived” attribute, then added the other necessary elements. I’ve still added the Comments element even though it will be empty. Depending on the rules that have been set about how I should layout the XML it might be optional.

To check that my code worked I plugged it into LINQPad and dumped the value of entry like so:

1
entry.ToString().Dump();

The results showed me the following:

1
2
3
4
5
<Entry Archived="false">
   <Title>My Second Post</Title>
   <Body>Just a quick post.</Body>
   <Comments />
</Entry>

Wow, that’s exactly what we want. Even though we used a Boolean value instead of a String for the attribute, XElement was smart enough to display its value as a human readable string. The XML is also nicely formatted and readable. I added the call to ToString() to emphasise that it wasn’t LINQPad that was responsible for the improved formatting.

What we have done here is generate an XML fragment. Sometimes it is easier to think of large XML files as smaller fragments that can be handled independently.

So now all we have to do is find the Entries element and add our entry XElement to it like so.

1
blog.Element("Entries").Add(entry);

This will leave us with the final XML looking like this:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
<Blog>
   <Entries>
     <Entry Archived="false">
       <Title>My First Post</Title>
       <Body>I love LINQ. It's the best</Body>
       <Comments>
         <!-- TODO: Shouldn't comments have authors? -->
         <Comment>I love LINQ more</Comment>
         <Comment>LINQ is the way of the future.</Comment>
       </Comments>
     </Entry>
     <Entry Archived="false">
       <Title>My Second Post</Title>
       <Body>Just a quick post.</Body>
       <Comments />
     </Entry>
   </Entries>
</Blog>

What about our XML declaration?

You might be wondering why the ToString() method of XElement doesn’t include the XML declaration. Because XElement represents a fragment of XML which could appear anywhere in an XML document. If it included the XML declaration it would lose this flexibility. However there is a workaround if you are outputting to a final file.

1
2
var blogDump = new StringBuilder();
blog.Save(new StringWriter(blogDump));

The Save() method on XElement automatically adds an appropriate XML declaration, which is probably a good idea as it sorts out the complicated things like the encoding and XML version (which I’ve never seen as anything other than 1.0 to date). The Save() method can take either the name of a file (as a String), an XmlWriter or TextWriter. In the example above I’ve used a StringWriter (which is a subclass of TextWriter) to save XML to a StringBuilder object which I could then use to build a string containing the XML. Save() also takes a second parameter, SaveOptions which allows you to save your XML file without the extra whitespace that I’ve shown above. If you want to save those bytes it might be worth looking at this option.

Where do we go from here?

I haven’t yet decided what my next LINQ post will cover (although LINQ to Entities is high on the agenda), so I won’t promise anything here now. I have much more to say still about LINQ, so feel free to post in the comments suggestions for areas to cover in future posts and the areas you would like to see covered in more detail. So far this has been fairly introductory and we’ll be building towards more advanced topics over the coming weeks.

Comments