Comparing WCF Serialization Options

There are many ways to use serialization objects using WCF. Determining which mechanism to use for serialization depends on a number of factors. These include whether you want to share types or contracts, support existing .NET types, preserve references, and more.

DataContractSerializer

The default serialization mechanism for WCF is the DataContractSerializer. This class can be found in the System.Runtime.Serialization namespace. The DataContractSerializer is built to support the sharing of contracts based on XSD schema. It maps Common Language Runtime (CLR) types to types defined in XSD. This means that XSD is the common schema that is used to exchange data between two applications. For example, you could exchange data between a .NET and Java application using XSD. An example of this is using a string.

System.String

xs:String

java.lang.String

Java

Figure 6.2 XSD types

Notice that type information, other than XSD types, is not exchanged between a server and a client. So in Figure 6.2, the notion of either System.String or java.lang.String is not exchanged as a part of the communication. This allows either side to map XSD types to specific types in their respective environments. This works well for primitive types. Complex types then become an extension of primitive types. So how does one describe mapping a .NET CLR type to an XSD schema using the DataCon-tractSerializer?

As described in Chapter 2, the [DataContract] attribute can be used to mark a type as serializable. Members and properties can then be marked with the [DataMember] attribute as being part of the data contract. This is very much an opt-in scenario where the developer defines how the type is serialized. This means that the contract is explicit, unlike the XmlSerial-izer, which is very much an opt-out mode. Listing 6.1 shows an example of a complex type, Employee, using the DataContractSerializer. We will use the Employee type to examine the schema and the serialized output using the DataContractSerializer. This will form the basis for comparison with the other serialization mechanisms available with WCF.

Listing 6.1 Employee Class Using DataContractSerialization using System.Runtime.Serialization;

[DataContract]

public class Employee

private int employeelD; private string firstName; private string lastName;

public Employee(int employeelD, string firstName, string lastName) {

this.employeelD = employeelD; this.firstName = firstName; this.lastName = lastName;

[DataMember]

public int EmployeelD {

get { return employeelD; } set { employeelD = value; }

[DataMember]

public string FirstName {

get { return firstName; } set { firstName = value; }

[DataMember]

public string LastName {

The Employee complex type shown in Listing 6.1 is represented in an XSD schema in Listing 6.2.

Listing 6.2 Employee XSD Schema

<xs:schema xmlns:tns=http://schemas.datacontract.org/2004/07/ ^•elementFormDefault="qualified" targetNamespace= *-"http://schemas.datacontract.org/2004/07/" *-xmlns:xs="http://www.w3.org/2001/XMLSchema"> <xs:complexType name="Employee"> <xs:sequence>

<xs:element minOccurs="0" name="EmployeeID" type="xs:int" /> <xs:element minOccurs="0" name="FirstName" nillable="true"

type="xs:string" / <xs:element minOccurs="0" name="LastName" nillable="true" type="xs:string" /> </xs:sequence> </xs:complexType>

<xs:element name="Employee" nillable="true" type="tns:Employee" /> </xs:schema>

Listing 6.3 shows how the schema of the Employee class was exported.

Listing 6.3 Export XSD Schema using System.IO;

using System.Runtime.Serialization; using System.Xml.Schema;

namespace EssentialWCF {

class Program {

static void Main(string[] args) {

XsdDataContractExporter xsdexp =

new XsdDataContractExporter(); xsdexp.Options = new ExportOptions(); xsdexp.Export(typeof(Employee));

// Write out exported schema to a file using (FileStream fs = new FileStream("sample.xsd",

FileMode.Create)) foreach (XmlSchema sch in xsdexp.Schemas.Schemas()) sch.Write(fs);

The final task that forms the basis for our comparison with other serialization mechanisms is to serialize an Employee instance using the DataCon-tractSerializer. Listing 6.4 shows how this is done.

Listing 6.4 Serialization Using DataContractSerializer using System.IO;

using System.Runtime.Serialization;

namespace EssentialWCF {

class Program {

static void Main(string[] args) {

Employee e = new Employee(101,"Dohn","Doe"); FileStream writer = new FileStream("sample.xml",

FileMode.Create);

DataContractSerializer ser =

new DataContractSerializer(typeof(Employee));

ser.WriteObject(writer, e); writer.Close();

The serialized output from the DataContractSerializer of the Employee class is shown in Listing 6.5.

Listing 6.5 Serialized Employee Class Using DataContractSerializer

<Employee xmlns="http://schemas.datacontract.org/2004/07/" xmlns:i="http://www.w3.org/2001/XMLSchema-instance"> <EmployeeID>101</EmployeeID> <FirstName>Dohn</FirstName> <LastName>Doe</LastName> </Employee>

NetDataContractSerializer

The NetDataContractSerializer is an alternative serialization mechanism available in WCF that allows for the sharing of types. This class can be found in the System.Runtime.Serialization namespace. This serialization can be used when type fidelity is required between client and server. The NetDataContractSerializer supports type fidelity by adding additional information for CLR type information and reference preservation. Besides this, there is no difference between the NetDataContractSerializer and the DataContractSerializer.

The sharing of type information goes against the principles of sharing just contracts. Because of this the NetDataContractSerializer is not meant for designing services between different applications and should be used within the confines of a single application. This is also why the capability to use the NetDataContractSerializer was left out of WCF. This means that this feature is available only if you write additional code. How to enable this feature will be discussed in the "Sharing Type with the NetDataContractSerializer" section.

Let's look at a particular instance of an Employee class serialized using the DataContractSerializer and the NetDataContractSerializer. We already saw how to serialize the Employee contract using the DataContractSerializer in Listing 6.4. Listing 6.6 shows how to serialize the same class using the NetDataContractSerializer. Notice that the NetDataCon-tractSerializer does not require a type passed into the constructor. This is because the NetDataContractSerializer will determine the CLR type of the Employee class at runtime.

Listing 6.6 Serialization Using NetDataContractSerializer using System.IO;

using System.Runtime.Serialization;

namespace EssentialWCF

class Program {

static void Main(string[] args) {

Employee e = new Employee(101/':iohn"/'Doe"); FileStream writer =

new FileStream("sample.xml", FileMode.Create); NetDataContractSerializer ser =

new NetDataContractSerializer(); ser.WriteObject(writer, e); writer.Close();

Listing 6.7 shows the serialized output of the Employee class. Notice that the NetDataContractSerializer includes the name of the Assembly and Type of the type that was serialized. This additional information can be used to deserialize the XML into the specified type. This allows the same type to be used by both the client and the server. The other information that is different is the z:Id attribute on various elements. This has to do with reference types and whether references are preserved when the XML is deserialized. We will discuss how to preserve references in the "Preserving References and Cyclical References" section. The final observation is that the output contains more information than the output from the DataContractSerializer.

Listing 6.7 Serialized Employee Class Using NetDataContractSerializer

<Employee z:Id="1" z:Type="Employee" z:Assembly="DataContract,

^•Version=1.0.0.0, Culture=neutral, PublicKeyToken=null"

^•xmlns="http://schemas.datacontract.org/2004/07/"

^xmlns:i="http://www.w3.org/2001/XMLSchema-instance" ^•xmlns:z="http://schemas.microsoft.com/2003/10/Serialization/"> <EmployeeID>101</EmployeeID> <FirstName z:Id="2">Dohn</FirstName> <LastName z:Id="3">Doe</LastName> </Employee>

XmlSerializer

The XmlSerializer is the third option available for serialization in WCF. The XmlSerializer is a serialization mechanism already built in to .NET 2.0. There are several advantages to using the XmlSerializer, including support for existing .NET types, compatibility with ASP.NET Web Services, and the capability to shape the XML output.

WCF supports the XmlSerializer so that it can work with existing types, whereas the DataContractSerializer is specifically meant for use with new types. Support for existing types is often the case with existing applications or third-party components where you do not have the source code or you cannot recompile your application to support DataContract serialization. The XmlSerializer is also the serialization used by ASP.NET Web Services. This means that the XmlSerializer can be used to help convert ASP.NET Web Services to WCF. Finally, the XmlSerializer offers the most control over the serialized XML output and can be used in scenarios where the DataContractSerializer is not sufficient to shape the serialized XML.

There are three approaches to using the XmlSerializer. The first is to rely on the default serialization. The XmlSerializer requires a public constructor and serializes any public fields and/or public read/write properties. The assumption is that your class can be reconstituted by creating an instance of the class with a default constructor and then set the appropriate fields and properties. Although simple, this approach almost never works unless you design your classes to support this method of serialization. It also means that you cannot serialize any of the internals of a class without exposing it to the rest of the world. The second approach is to use the [XmlElement] and [XmlAttribute] attributes to mark up public fields and public read/write properties. These attributes, and more attributes to control the resulting XML, can be found in the System.Xml.Serialization namespace. The attribute approach allows for control over how public fields and public read/write properties are expressed in XML. Although simple, this approach still limits how you can serialize your objects and may force you to expose internal data structures that you would otherwise not expose to a consumer of your class. The third approach is to use the IXmlSerializable interface to completely customize the serialization using the XmlSerializer. This approach allows for complete customization of the serialization process.

We will take a look at custom serialization using the XmlSerializer later in this chapter. For now we will concentrate on the simplest example using the XmlSerializer. Listing 6.8 uses the XmlSerializer using the same Employee class without support for DataContractSerializer by removing the [DataContract] or [DataMember] attributes present in Listing 6.1. A default constructor is also needed.

Listing 6.8 Employee Class Using XmlSerializer public class Employee {

private int employeelD; private string firstName; private string lastName;

public Employee() {

public Employee(int employeelD, string firstName, string lastName) {

this.employeelD = employeelD; this.firstName = firstName; this.lastName = lastName;

public int EmployeelD {

get { return employeelD; } set { employeelD = value; }

public string FirstName {

get { return firstName; } set { firstName = value; }

public string LastName

Listing 6.9 shows how to serialize the Employee instance using the XmlSerializer.

Listing 6.9 Serialization Using XmlSerializer using System.IO;

using System.Xml.Serialization;

namespace EssentialWCF {

class Program {

static void Main(string[] args) {

Employee e = new Employee(101/':]ohn"/'Doe"); FileStream writer =

new FileStream("sample.xml", FileMode.Create); XmlSerializer ser = new XmlSerializer((typeof(Employee))); ser.Serialize(writer, e); writer.Close();

The output from the XmlSerializer is shown in Listing 6.10. Notice that the output is similar to the output from the DataContractSerializer. Both serialization mechanisms output XML that looks similar. The big difference is in what is not shown. The XmlSerializer does not support as many types as the DataContractSerializer but allows for greater control over the resulting XML.

Listing 6.10 Serialized Employee Class Using XmlSerializer

<Employee xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema"> <EmployeeID>101</EmployeeID> <FirstName>Dohn</FirstName> <LastName>Doe</LastName> </Employee>

The Employee class in Listing 6.8 is not serializable without the XmlSerializer. The [XmlSerializerFormat] attribute can be used to attribute a service contract, operation contract, or service to instruct WCF to use the XmlSerializer. Listing 6.11 uses the [XmlSerializerFormat] attribute on a service contract to instruct WCF to use the XmlSerializer. You will typically want to apply the [XmlSerializerFormat] attribute if you are exposing any contract that uses the XMLSerializer. Doing so instructs Visual Studio and the svcutil.exe tool to generate proxies that rely on the XMLSerializer. Without this attribute you need to generate proxies using the svcutil.exe tool with the /Serializer:XmlSerializer flag.

■ TIP The DataContractFormat Attribute

Conversely, there is an equivalent attribute to the [XmlSerializerFormat] attribute for the DataContractSerializer called the [DataContractFormat] attribute. WCF uses the DataContractSerializer by default, so there should be no reason to use this attribute.

Listing 6.11 Using XmlSerializerFormat Attribute using System.Collections.Generic; using System.ServiceModel;

namespace EssentialWCF {

[ServiceContract] [XmlSerializerFormat]

public interface IEmployeelnformation {

[OperationContract] List<Employee> GetEmployees();

DataContractDsonSerializer

The DataContractDsonSerializer supports the use of JavaScript Object Notation as a serialization format and is available with .NET Framework 3.5. This serialization works well if you are calling services from a Web application using JavaScript, especially ASP.NET AJAX and Silverlight Web applications. The DataContractDsonSerializer is used when the WebScriptEnablingBehavior behavior is used. Alternatively, it can be used if the WebHttpBehavior behavior is configured to use JSON encoding. These endpoint behaviors instruct WCF to support REST/POX style services. See Chapter 13, "Programmable Web," for more information about these attributes. For now, we will examine how to use the DataContractDsonSerial-izer directly and compare it to the other serialization mechanisms mentioned previously. Listing 6.12 shows how to serialize an Employee instance using the DataContractDsonSerializer.

Listing 6.12 Serialization Using DataContractDsonSerializer using System.IO;

using System.Runtime.Serialization.Dson;

namespace EssentialWCF

class Program {

static void Main(string[] args) {

Employee e = new Employee(101/'Dohn"/'Doe"); FileStream writer = new FileStream("sample.xml",

FileMode.Create); DataContractDsonSerializer ser =

new DataContractDsonSerializer(typeof(Employee)); ser.WriteObject(writer, e); writer.Close();

The DataContractDsonSerializer follows the same rules of serialization as the DataContractSerializer except that the output is JSON and not XML. The serialized output from the DataContractDsonSerializer of the Employee class is shown in Listing 6.13. The output in this case is much smaller and more compact than using the DataContactSerializer, NetDataContractSerializer, or the XmlSerializer, and the output is more readable compared to the previous XML examples.

Listing 6.13 Serialized Employee Class Using DataContractDsonSerializer

{"EmployeeID":101,

"FirstName'VDohn",

"LastName":"Doe"}

0 0

Post a comment

  • Receive news updates via email from this site