Click here to Skip to main content
15,867,308 members
Articles / Web Development / ASP.NET

Having Fun with the XmlSchemaProvider: Serializing Object Trees

Rate me:
Please Sign up or sign in to vote.
4.85/5 (11 votes)
9 May 20063 min read 77K   532   25   17
Learn how to serialize object trees into SOAP and back again

Introduction

About a year ago, I became very enthusiastic. I found a new attribute in the framework which allows me to determine the WSDL contract which describes my objects. Used in conjunction with the IXmlSerializable interface, I can also make sure the serializer pumps out the correct XML, corresponding to the custom XML-Schema inside the WSDL. Why is this so important? The default XmlSerializer requires all fields to have public get / set properties. This means my Product object needs a public get/set ID property. I personally didn’t want a settable ID property, because it is the never-changing primary key for that object. Furthermore, I did not want to change the public interface of my objects just because they’ll be serialized into a SOAP message. Using XmlSchemaProvider with IXmlSerializable, you can fix all this; keep only the getter, and still serialize the ID.

Now, all of this sounds good of course, and there are various samples on the internet showing how you can use these two classes to serialize your own ‘Product’ objects. However, the examples I found only show how to serialize a single object, not a tree of objects, such as an Order-OrderItem combination or Order-Customer-Address. Is this more difficult then? Well, yeah, a tad-bit, because you’ll have to share the XML-Schema definition somehow.

Not finding the answer on the web, I dug into this problem myself, like I did about a year ago. The only difference is that this time, I have a working solution which I want to share with you guys.

Serializing an Order Object

The following example will show how you can utilize XmlSchemaProvider for serializing an object-tree. This example uses two objects, Order and Product, with an Order containing multiple Products. The Product class contains a readonly ID property, and has a private default constructor. The Order class contains a readonly List<T> of Products and also a readonly ID property. Here, I’ll give attention only to the Order class, the Product class is not that interesting after reading up on the subject. Check the code to see how it has been built.

The Order class I’ll describe a bit further, since that’s where the magic happens.

The basic class definition for Order is as follows:

C#
[XmlSchemaProvider("GetOrderSchema")]
public class Order : IXmlSerializable
{
    int _id;
    List<Product> _products = null;

    public int ID { get;}
    public List<Product> Products { get;}
}

To supply the correct schema to the serialization infrastructure, the XmlSchemaProvider attribute is applied to the class. Inside the method tied to the XmlSchemaProvider attribute, the schema for the Order class is constructed using the System.Xml object model, also the Product is included in the schema for the Order class using an XmlSchemaInclude:

C#
public static XmlSchemaComplexType GetOrderSchema(
              XmlSchemaSet schemas)
{
    string tns = "http://diveinit.nl/schemas/test";
    string xmlns = "http://www.w3.org/2001/XMLSchema";

    XmlSchema schema = new XmlSchema();
    // Include the product schema as an include so it can
    // be referenced in the order schema.
    XmlSchema productSchema = Product.Schema;
    XmlSchemaInclude productSchemainclude =
        new XmlSchemaInclude();
    productSchemaInclude.Schema = productSchema;
    schema.Includes.Add(productSchemaInclude);
    ...
    ...
  //-- The rest of the method is ommited for brevity--//
}

Now that the WSDL has been set up correctly, it is time to serialize and deserialize the Order class. This is done using the IXmlSerializable interface, which requires you to build three methods. First up is the serialization using the WriteXml method. This method is also responsible for serializing the Product objects contained in the Order.

C#
void IXmlSerializable.WriteXml(XmlWriter writer)
{
    string ns = "http://diveinit.nl/schemas/test";
    writer.WriteElementString("id", ns, _id.ToString());
    ICollection<Product> products = Products;
    if (products.Count > 0)
    {
        writer.WriteStartElement("ProductCollection");
        foreach (Product product in products)
        {
            writer.WriteStartElement("Product");
            ((IXmlSerializable)product).WriteXml(writer);
            writer.WriteEndElement();
        }
        writer.WriteEndElement();
    }
}

The root element is supplied for you, this means you don’t have to write this yourself inside the WriteXml method. So the first item to write is the private ID field of the Order object. Next up are all the Products contained in the Order. They are contained inside an extra element called "ProductCollection". Make sure to use the WriteXml method of the Product object so that is serialized correctly too.

Now for some deserialization. This is a bit more difficult because you do not know how many Products the Order contains, when deserializing. The XmlReader provides a handy method to determine this, which is called IsStartElement.

C#
void IXmlSerializable.ReadXml(XmlReader reader)
{
    string ns = "http://diveinit.nl/schemas/test";
    reader.ReadStartElement();
    _id = reader.ReadElementContentAsInt("id", ns);
    if (reader.IsStartElement("ProductCollection", ns))
    {
        reader.ReadStartElement();
        while (reader.IsStartElement("Product", ns))
        {
            Product product = new Product();
            ((IXmlSerializable)product).ReadXml(reader);
            Products.Add(product);
        }
    }
    reader.ReadEndElement();
}

That is it. A fully functional example on how to use XmlSchemaProvider in conjunction with IXmlSerializable for all your serialization needs!

License

This article has no explicit license attached to it, but may contain usage terms in the article text or the download files themselves. If in doubt, please contact the author via the discussion board below.

A list of licenses authors might use can be found here.


Written By
Web Developer Code Counsel
Netherlands Netherlands
Wouter van Vugt is a Microsoft MVP with Office Open XML technologies and an independent consultant focusing on creating Office Business Applications (OBAs) with SharePoint, the Office 2007 system and related .NET technologies. Wouter is a frequent contributor to developer community sites such as OpenXmlDeveloper.org and MSDN and has published several white papers and articles as well a book available on line titled Open XML: the markup explained. Wouter is the founder of Code-Counsel, a Dutch company focusing on delivering cutting-edge technical content through a variety of channels. You can find out more about Wouter by reading his blog and visiting the Code-Counsel Web site.

Comments and Discussions

 
GeneralTrying to build public static XmlQualifiedName GetMySchema(XmlSchemaSet xs) Pin
Howard ( Chaim) Davis18-Feb-10 5:43
Howard ( Chaim) Davis18-Feb-10 5:43 
QuestionBug? Pin
zux20-Nov-08 22:14
zux20-Nov-08 22:14 
AnswerRe: Bug? Pin
zux20-Nov-08 22:36
zux20-Nov-08 22:36 
GeneralRe: Bug? [modified] Pin
ishimakmola15-Mar-09 10:29
ishimakmola15-Mar-09 10:29 
QuestionHow to reuse a child schema in multiple parents? Pin
wout de zeeuw1-Mar-07 10:48
wout de zeeuw1-Mar-07 10:48 
Hi Wouter,

Awesome article, I'm putting it to practise doing my own serialization!

Anyways, here are some deeper issues I ran into that you might have mitigated already:

- Suppose there's a child schema A, and there are 2 parent schema's that have A as a child. How do you prevent adding the same A schema to the XmlSchemaSet?
- What's the best way to include schema's for .NET types like System.Guid? (they are in namespace http://microsoft.com/wsdl/types/):

Wout

QuestionHave ReadEndElement forgoten or not? Pin
merkuriev_sergey6-Feb-07 13:02
merkuriev_sergey6-Feb-07 13:02 
General2 ways to make service added through VS work [modified] Pin
ada cai20-Dec-06 1:38
ada cai20-Dec-06 1:38 
GeneralSerialize/Deserialize to String Pin
smolesen16-Nov-06 3:00
smolesen16-Nov-06 3:00 
Generalthanks Pin
Andreas Hollmann16-Jul-06 4:21
Andreas Hollmann16-Jul-06 4:21 
GeneralMixing the Serializable Attribute and IXmlSerializable Pin
Serving4Him5-Jun-06 12:29
Serving4Him5-Jun-06 12:29 
GeneralRe: Mixing the Serializable Attribute and IXmlSerializable Pin
Wouter van Vugt6-Jun-06 23:40
Wouter van Vugt6-Jun-06 23:40 
QuestionRe: Mixing the Serializable Attribute and IXmlSerializable Pin
Serving4Him8-Jun-06 9:19
Serving4Him8-Jun-06 9:19 
GeneralSharing implementation Pin
Markus Rennings17-May-06 6:53
Markus Rennings17-May-06 6:53 
GeneralRe: Sharing implementation Pin
Wouter van Vugt17-May-06 9:07
Wouter van Vugt17-May-06 9:07 
GeneralRe: Sharing implementation Pin
Markus Rennings17-May-06 23:08
Markus Rennings17-May-06 23:08 
GeneralRe: Sharing implementation Pin
Wouter van Vugt17-May-06 23:11
Wouter van Vugt17-May-06 23:11 
Generalvery nice! Pin
spamcatcher66617-May-06 6:35
spamcatcher66617-May-06 6:35 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.