DataContracts without attributes (POCO support) in .NET 3.5 SP1


One of the
new WCF features in .NET 3.5 SP1 is that DataContractSerializer now supports serializing types that aren’t annotated with any serialization attributes like [DataContract] or [Serializable].

If you were using DataContractSerializer prior to SP1, you had to follow the rules outline by Sowmy here. These rules illustrate that for custom classes you have a few choices. You can annotate the class with [DataContract] and [DataMember] to define an attribute-based mapping or implement IXmlSerializable to define a custom mapping. Or you can annotate the class with [Serializable] to automatically map all fields (like with .NET Remoting) or implement ISerializable to take things into your own hands (assuming IXmlSerializable wasn't used).

However, as you can see from the rules, there is no allowance for types that haven’t been annotated with one of these serialization attributes or that implement one of the serialization-related interfaces, or in other words, you can't serialize “plain old C# objects“ (POCO for short).

The support for [Serializable] provided a nice migration path for traditional .NET Remoting types, which was nice, but the lack of support for POCO types meant you couldn’t move your ASMX types over to the DataContractSerializer without sprinkling a bunch of new attributes on them.

With .NET 3.5 SP1 you can serialize any C# object even if it doesn’t come with any serialization attributes. For example, the following Person type is now serializable by default:

namespace SerializationSp1

{

    public class Person

    {

        private string id;

        public string Id { get { return id; } set { id = value; } }

        public string Name;

        public Person Spouse;

    }

    ...

}

 

For POCO types, DataContractSerializer only includes the public read/write fields and properties into the resulting XML Infoset. So in our example above, the private “id” field won’t make it into the message. Also, these types must have a public default (no argument) constructor in order to be serialized. The Person type above works find because the compiler gives us a public default constructor but if you were to add a non-default constructor, it would no longer be serializable using this approach.

With this new support, you can use virtually any C# type (with a public default constructor) in your WCF service contracts and you don’t have to worry about changing the serializer back to XmlSerializer using [XmlSerializerFormat]. For example, the following service contract works as-is in .NET 3.5 SP1:

[ServiceContract]

public interface ILookupPerson

{

    [OperationContract]

    Person GetPerson(string id);

}

 

Now let’s take a look at the serialized XML for the Person type shown above. Here’s a simple console program that uses DataContractSerializer to serialize a Person object:

class Program

{

    static void Main(string[] args)

    {

        Person p = new Person();

        p.Id = "123";

        p.Name = "Aaron";

        p.Spouse = new Person();

        p.Spouse.Id = "456";

        p.Spouse.Name = "Monica";

 

        DataContractSerializer dcs = new DataContractSerializer(typeof(Person));

        using (FileStream fs = new FileStream("person.xml", FileMode.Create))

        {

            dcs.WriteObject(fs, p);

        }

    }

}

And here’s what the resulting person.xml file looks like:

<Person xmlns="http://schemas.datacontract.org/2004/07/SerializationSp1"

        xmlns:i="http://www.w3.org/2001/XMLSchema-instance">

  <Id>123</Id>

  <Name>Aaron</Name>

  <Spouse>

    <Id>456</Id>

    <Name>Monica</Name>

    <Spouse i:nil="true"/>

  </Spouse>

</Person>

The mapping algorithm is similar to what’s used with [DataContract] or [Serializable] – it uses the type name for the root element name, member names for the local element names, and it orders them alphabetically. It also produces a reasonable XML namespace based on the .NET namespace. The only difference is how it chooses what to put into the message – in this case it’s based on the public contract of the type. When you use this approach, you must be happy with the XML that DataContractSerializer gives you. In other words, you can’t customize the resulting XML in any way.

As soon as you place the [DataContract] attribute on the class, DataContractSerializer will only include fields/properties annotated with [DataMember] once again. For example, suppose I make the following change to the Person type by annotating it with [DataContract]:

[DataContract]

public class Person

{

    private string id;

    public string Id { get { return id; } set { id = value; } }

    public string Name;

    public Person Spouse;

}

 

If I run my console program again, the resulting person.xml now looks like this:

<Person xmlns="http://schemas.datacontract.org/2004/07/SerializationSp1"  
       
xmlns:i="http://www.w3.org/2001/XMLSchema-instance"/>

Notice that none of the fields were serialized because they weren’t annotated with [DataMember]. Once I applied [DataContract] to Person, DataContractSerializer no longer treated it like a POCO type. The same would hold true if I annotated the type with [Serializable]. If it finds the [Serializable] attribute, it falls back to the [Serializable] mapping and only includes the fields.

To summarize, the new DataContractSerializer provides several different mechanisms for defining the serialization mapping:

1.        Simply rely on the public interface and take the default XML mapping

2.        Use [Serializable] to only include fields in the mapping

3.        Use [DataContract] and [DataMember] and apply some basic customization

4.        Use IXmlSerializable or ISerializable for advanced mapping customization

I was actually surprised to learn that they added this feature because it goes against the main reason for the original [DataContract] design (“boundaries are explicit”), at least according to the team in early design reviews. I asked for this feature (an implicit mapping) but my request was dismissed for that very reason. Despite whatever principle it may violate, I like it, because it makes it simpler for folks to get started with WCF and it provides an easier migration path for ASMX.


Posted May 13 2008, 02:50 PM by aaron-skonnard
Filed under:

Comments

dominick wrote re: DataContracts without attributes (POCO support) in .NET 3.5 SP1
on 05-13-2008 11:31 PM
Is that Person a business object or a DTO??
Aaron Skonnard wrote re: DataContracts without attributes (POCO support) in .NET 3.5 SP1
on 05-14-2008 7:28 AM
It's a DTO -- I updated the post this morning with a few changes to (hopefully) make things clearer...
Haacked wrote re: DataContracts without attributes (POCO support) in .NET 3.5 SP1
on 05-14-2008 8:29 AM
My guess is so you can serialize anonymous types.

For example, Serialize(new {foo="bar"});

Requiring annotations would prevent you from serializing the results of linq to object queries.
Aaron Skonnard wrote re: DataContracts without attributes (POCO support) in .NET 3.5 SP1
on 05-14-2008 9:42 AM
You're probably right about anonymous types. DataContractSerializer was probably also overly complicated for folks on the ASP.NET front doing Javascript serialization, which I hadn't thought about while writing this, so that could be another reason.
Anderson Imes wrote re: DataContracts without attributes (POCO support) in .NET 3.5 SP1
on 05-15-2008 11:41 AM
If I decorate my type with [DataContract] are members that I've not decorated with [DataMember] still serialized without my consent? In other words, is DataMember now opt-out, rather than opt-in?
Anderson Imes wrote re: DataContracts without attributes (POCO support) in .NET 3.5 SP1
on 05-15-2008 11:42 AM
Nevermind - I just saw that comment.... *whew*.
Rob Eisenberg wrote re: DataContracts without attributes (POCO support) in .NET 3.5 SP1
on 05-21-2008 6:49 AM
Actually POCO stands for Plain Old CLR Object ;)
Craig Dunn wrote re: DataContracts without attributes (POCO support) in .NET 3.5 SP1
on 05-21-2008 3:18 PM
I had weird behaviour with [Serializable] when upgrading something from ASP.NET ASMX to WCF 3.5 - Automatic Properties were given their 'compiler name' <Id>k__BackingField in JSON.

http://conceptdev.blogspot.com/2008/02/wcf-web-services-serialization-of.html

*Without SP1* I _had_ to include [DataContract] [DataMember] attributes to get the correct property name serialized.

Will have to give it a try with SP1...
Mark wrote re: DataContracts without attributes (POCO support) in .NET 3.5 SP1
on 05-22-2008 4:28 AM
Is there any way to get rid of the standard attributes that the DataContractSerializer outputs? - i.e:

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

and:

xmlns:i="http://www.w3.org/2001/XMLSchema-instance
Aaron Skonnard wrote re: DataContracts without attributes (POCO support) in .NET 3.5 SP1
on 05-22-2008 7:26 AM
Craig,

The reason you were getting the backing fields and not the properties was because you annotated the class with [Serializable] and that's the correct default mapping for [Serializable] when using DataContractSerializer (the default in WCF).

However, with this new POCO support in SP1, if you remove [Serializable], you should get the same behavior in both scenarios.

Add a Comment

(required)  
(optional)
(required)  
Remember Me?