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!
- If the data contract type is a derived class, the members of the base type will always be serialized first.
- If no order is specified, the members will be serialized in alphabetical order based on an ordinal comparison.
- 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.