WCF: Serialization Order in Data Contracts

8. May 2007 10:00

When working with data contracts, you may come across a situation where there is a preference to the order in which members are serialized.  It could be due to some dependency on the value that requires them to be serialized in a particular order.  Or, maybe you are just very picky about the appearance of the the resulting xml.  Regardless, there will likely be some occasions where the default serialization order doesn't work for you.

What is the default serialization order?  I'm glad you asked!

  1. If the data contract type is a derived class, the members of the base type will always be serialized first.
  2. If no order is specified, the members will be serialized in alphabetical order based on an ordinal comparison.
  3. Any members with a specified order will be serialized from least to greatest.  If two members share the same order, the serialization order will be determined by Rule #2.

Let's look at an example to put this into context.

Here is a simple service interface that provides a method for retrieving book information. 

[ServiceContract(Namespace = "http://jeffbarnes.net/2007/05/bookservice/")]
public interface IBookService
{
    [OperationContract]
    Book GetBook();
}

Now, let's examine the data contract for the Book class hierarchy:

[DataContract(Namespace = "http://jeffbarnes.net/2007/05/bookservice/book/")]
[KnownType(typeof(ModernBook)]
public class Book
{
    [DataMember]
    public string Name;

    [DataMember]
    public string Author;

    [DataMember]
    public string Publisher;

    [DataMember]
    public DateTime ReleaseDate;

    [DataMember]
    public string Isbn;

    [DataMember]
    public int NumberOfPages;    

    public Book()
    {
    }
}

[DataContract(Namespace = "http://jeffbarnes.net/2007/05/libraryservice/modernbook/")]
public class ModernBook : Book
{
    [DataMember]
    public string Isbn13;

    public ModernBook() : base()
    {
    }
}

Notice there is no order specified anywhere in the data contract.  After setting up the necessary service diagnostics, it is possible to log the actual SOAP messages that are exchanged between the client and server.  Here is the SOAP message returned for the data contract based on the service implementation:

<s:Envelope xmlns:a="http://www.w3.org/2005/08/addressing" xmlns:s="http://www.w3.org/2003/05/soap-envelope">
    <s:Header>
        <a:Action s:mustUnderstand="1">http://jeffbarnes.net/2007/05/bookservice/IBookService/GetBookResponse</a:Action>
    </s:Header>
    <s:Body>
    <GetBookResponse xmlns="http://jeffbarnes.net/2007/05/bookservice/">
        <GetBookResult xmlns:d4p1="http://jeffbarnes.net/2007/05/bookservice/book/" xmlns:i="http://www.w3.org/2001/XMLSchema-instance" xmlns:d4p3="http://jeffbarnes.net/2007/05/libraryservice/modernbook/" i:type="d4p3:ModernBook">
            <d4p1:Author>Juval Lowy</d4p1:Author>
            <d4p1:Isbn>0596526997</d4p1:Isbn>
            <d4p1:Name>Programming WCF Services</d4p1:Name>
            <d4p1:NumberOfPages>634</d4p1:NumberOfPages>
            <d4p1:Publisher>O'Reilly Media, Inc.</d4p1:Publisher>
            <d4p1:ReleaseDate>2007-02-20T00:00:00</d4p1:ReleaseDate>
            <d4p3:Isbn13>978-0596526993</d4p3:Isbn13>
        </GetBookResult>
    </GetBookResponse>
    </s:Body>
</s:Envelope>

 As you can see, the members of the Book and ModernBook types serialized as outlined in the previously mentioned rules.  Now, let's specify an order to the data members and see how it looks.  Here are the revised data contracts:

[DataContract(Namespace = "http://jeffbarnes.net/2007/05/bookservice/book/")]
[KnownType(typeof(ModernBook)]
public class Book
{
    [DataMember(Order = 0)]
    public string Name;

    [DataMember(Order = 1)]
    public string Author;

    [DataMember(Order = 4)]
    public string Publisher;

    [DataMember(Order = 5)]
    public DateTime ReleaseDate;

    [DataMember(Order = 2)]
    public string Isbn;

    [DataMember(Order = 6)]
    public int NumberOfPages;    

    public Book()
    {
    }
}

[DataContract(Namespace = "http://jeffbarnes.net/2007/05/libraryservice/modernbook/")]
public class ModernBook : Book
{
    [DataMember(Order = 3)]
    public string Isbn13;

    public ModernBook() : base()
    {
    }
}

Notice that the Order property was simply added to specify the order in which serialization should occur.  Once again, here is the SOAP message that results from the data contract:

<s:Envelope xmlns:a="http://www.w3.org/2005/08/addressing" xmlns:s="http://www.w3.org/2003/05/soap-envelope">
    <s:Header>
        <a:Action s:mustUnderstand="1">http://jeffbarnes.net/2007/05/bookservice/IBookService/GetBookResponse</a:Action>
    </s:Header>
    <s:Body>
        <GetBookResponse xmlns="http://jeffbarnes.net/2007/05/bookservice/">
            <GetBookResult xmlns:d4p1="http://jeffbarnes.net/2007/05/bookservice/book/" xmlns:i="http://www.w3.org/2001/XMLSchema-instance" xmlns:d4p3="http://jeffbarnes.net/2007/05/libraryservice/modernbook/" i:type="d4p3:ModernBook">
                <d4p1:Name>Programming WCF Services</d4p1:Name>
                <d4p1:Author>Juval Lowy</d4p1:Author>
                <d4p1:Isbn>0596526997</d4p1:Isbn>
                <d4p1:Publisher>O'Reilly Media, Inc.</d4p1:Publisher>
                <d4p1:ReleaseDate>2007-02-20T00:00:00</d4p1:ReleaseDate>
                <d4p1:NumberOfPages>634</d4p1:NumberOfPages>
                <d4p3:Isbn13>978-0596526993</d4p3:Isbn13>
            </GetBookResult>
        </GetBookResponse>
    </s:Body>
</s:Envelope>

As you can see, this time the serialization occurred in the order specified within the data contracts.  If you look closely, you will notice one potentially unexpected issue.  In the ModernBook class, an Order of 3 was specified for Isbn13.  However, Isbn13 is still the last member that was serialized since it was at the bottom.  This is due to ModernBook being a derived class of Book.  Since it is lower in the inheritance hierarchy, it will still be serialized after the Book members. 

This is a limitation of the DataContract.  It doesn't support the ability to serialize derived class members prior to base class members.  In most cases, this is probably acceptable.  However, you may run into a situation where you really want derived class members to serialize prior to base class members.  If so, you will have to use a more customized serialization method, which will be covered later in the week.

The example code can be downloaded here.

Comments

5/7/2007 7:47:43 PM #

http://

Hi Jeff,

Looking at the order specified and the order in the xml, shouldn't Name go before Author having Order=0?  Or am I missing something here?

Thanks,
Ken

http:// |

5/8/2007 3:26:50 AM #

http://

Ken:

You are correct.

I pasted the wrong SOAP snippet into my post.  It was an earlier version from when I was writing some sample code.  I ended up changing the order for the version that I used as an example.  I have modified the post to use the correct SOAP envelope.

Thanks for the catch.

http:// |

Comments are closed

About Me

I'm a passionate software developer and advocate of the Microsoft .NET platform.  In my opinion, software development is a craft that necessitates a conscious effort to continually improve your skills rather than falling into the trap of complacency.  I was also awarded as a Microsoft MVP in Connected Systems in 2008, 2009, and 2010.


Can’t code withoutThe best C# & VB.NET refactoring plugin for Visual Studio
Follow jeff_barnes on Twitter

View Jeff Barnes's profile on LinkedIn

 

Shared Items

Disclaimer

Anything you read or see on this site is solely based on my own thoughts.  The material on this site does not necessarily reflect the views of my employer or anyone else.  In other words, I don't speak for anyone other than myself.  So, don't assume I am the official spokesperson for anyone.