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.
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.
About Newsletter
Comments are closed.