Scott Hanselman

Mixing XmlSerializers with XElements and LINQ to XML

August 28, '07 Comments [0] Posted in Learning .NET | LINQ | Programming | XML | XmlSerializer
Sponsored By

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.

About Scott

Scott Hanselman is a former professor, former Chief Architect in finance, now speaker, consultant, father, diabetic, and Microsoft employee. He is a failed stand-up comic, a cornrower, and a book author.

facebook twitter subscribe
About   Newsletter
Sponsored By
Hosting By
Dedicated Windows Server Hosting by ORCS Web
Comments are closed.

Disclaimer: The opinions expressed herein are my own personal opinions and do not represent my employer's view in any way.