You are on page 1of 1

Mixing XmlSerializers with XElements and LINQ to XML

I used to mix and match XmlWriters and XmlSerializers when I had objects that I wanted to serialize in the middle
of a larger chunk of XmlWriter-generated XML, like this, where the variable a is typeof(Author).
using (XmlWriter writer =
XmlWriter.Create(Response.OutputStream, settings))
{
//Note the artificial, but useful, indenting
writer.WriteStartDocument();
writer.WriteStartElement("bookstore");
writer.WriteStartElement("book");
writer.WriteStartAttribute("publicationdate");
writer.WriteValue(publicationdate);
writer.WriteEndAttribute();
writer.WriteStartAttribute("ISBN");
writer.WriteValue(isbn);
writer.WriteEndAttribute();
writer.WriteElementString("title", "ASP.NET 2.0");
writer.WriteStartElement("price");
writer.WriteValue(price);
writer.WriteEndElement(); //price
XmlSerializer xs = factory.CreateSerializer(typeof(Author));
xs.Serialize(writer, a);
writer.WriteEndElement(); //book
writer.WriteEndElement(); //bookstore
writer.WriteEndDocument();
}
See how there XmlSerializer just writes directly to the XmlWriter and the object is serialized in the middle of the
XmlWriter calls? I found this very useful and used it often.
I wanted to do the same thing with LINQ to XML but was ever so frightened. Earlier I punted and just created the
XML myself as an XElement tree. See the author "a" in the middle there?:
XNamespace ns = "http://example.books.com";
XDocument books = new XDocument(
new XElement(ns + "bookstore",
new XElement(ns + "book",
new XAttribute("publicationdate", publicationdate),
new XAttribute("ISBN", isbn),
new XElement(ns + "title", "ASP.NET 2.0 Book"),
new XElement(ns + "price", price),
new XElement(ns + "author",
new XElement(ns + "first-name", a.FirstName),
new XElement(ns + "last-name", a.LastName)
)
)
)
);
Ion on the XmlTeam explained that the XmlWriter returned by calls to CreateWriter on both XDocument and
XElement classes is special. After your done using the XmlWriter and it's Close()d, it will take all the generated
XML and Add() it to the parent/owner XDocument or XElement.
So, now I can make an extension method and add my SerializeAsXElement method to XmlSerializer:
static class XmlSerializerExtension {
public static XElement SerializeAsXElement(this XmlSerializer xs, object o) {
XDocument d = new XDocument();
using (XmlWriter w = d.CreateWriter()) xs.Serialize(w, o);
XElement e = d.Root;
e.Remove();
return e;
}
}
Notice the use of using to ensure the XmlWriter is close (Close is called in the Dispose of XmlWriter) and that the
root element is removed from the document. Ion explains that "This avoids the cloning of the returned element
during any subsequent Add() (eg. functional construction)."
Now, I can use the extension method in the middle of my XElement expression.
XmlSerializer xs = new XmlSerializer(typeof(Author));
XDocument books = new XDocument(
new XElement(ns + "bookstore",
new XElement(ns + "book",
new XAttribute("publicationdate", publicationdate),
new XAttribute("ISBN", isbn),
new XElement(ns + "title", "ASP.NET 2.0 Book"),
new XElement(ns + "price", price),
xs.SerializeAsXElement(a)
)
)
);
And it produces XML identical to the XmlWriter example at the beginning. Big thanks to Ion Vasilian (brilliant
moderator of the LINQ Project forums and XmlTeam blogger) for all the help with this question.