Partilhar via


Serializing Plain Old CLR Object (POCO) types with DataContractSerializer

When version 3.0 of the .NET framework shipped, DataContractSerializer could only support a few categories of CLR types. Sowmy has a good list of these that you can find here. The recommended model was to use DataContractAttributes on classes and structs along with DataMemberAttributes on fields and properties to customize the serialization of types. I really like that model because it forces users to think about the data they’re passing on the wire. However, adding attributes to every class and every member can be very tedious. It also meant that types that could be serialized by XmlSerializer couldn’t be serialized at all by DataContractSerializer.

 

To remedy the issue, POCO serialization was introduced in .NET 3.5 SP1. What this means is that you can still serialize custom types without attributes with DataContractSerializer provided the type is public and has a parameterless constructor. Remember though that the C# compiler provides you with a default parameterless constructor if you don’t specify one, so you may not need to explicitly write a constructor for a class to make it POCO-serializable. The visibility of the parameterless constructor doesn't matter so it can be public/internal/private, just as long as there is one. If this rule sounds familiar, it’s because that’s exactly how XmlSerializer works.

 

So whereas the type:

 

public class POCOBook

{

    public string Title { get; set; }

    public int Year { get; set; }

}

Couldn’t be serialized out in 3.0, it can be serialized with DataContractSerializer in 3.5 SP1. However, if you removed the default constructor the C# compiler gives you by adding another constructor:

 

public class POCOBook

{

    public POCOBook(string s) { }

    public string Title { get; set; }

    public int Year { get; set; }

}

 

Then you get the familiar exception string:

 

Type 'POCOBook' cannot be serialized. Consider marking it with the DataContractAttribute attribute, and marking all of its members you want serialized with theDataMemberAttribute attribute. See the Microsoft .NETFramework documentation for other supported types.

 

When you try serializing it out. You would also get the same exception if you made the POCOBook class internal. So how does the serializer decide which members to serialize out? Well again, this works just like XmlSerializer. All public fields and properties will be serialized out. POCOBook on the wire looks like this:

 

<POCOBook xmlns="https://schemas.datacontract.org/2004/07/" xmlns:i="https://www.w3.org/2001/XMLSchema-instance" xmlns:z="https://schemas.microsoft.com/2003/10/Serialization/">

  <Title>Ender's Game</Title>

  <Year>1985</Year>

</POCOBook>

 

Notice how both Title and Year were serialized out. It’s also possible to slightly tweak this by using the IgnoreDataMemberAttribute to opt out a particular member from being serialized. Rewriting POCOBook to look like this:

 

public class POCOBook

{

    public string Title { get; set; }

    [IgnoreDataMember]

    public int Year { get; set; }

}

 

Would mean that only Title would be serialized out.

 

There are a couple important notes that need to be made about POCO serialization:

 

POCO serialization will only kick in as a last resort for the serializer. So if a type is marked with DataContractAttribute or CollectionDataContractAttribute, it will be serialized according to data contract rules. If the type is marked with SerializableAttribute, it will be serialized as a serializable type. If the type implements IXmlSerializable or ISerializable, those custom serialization implementations will be used instead. And if the type is a collection, instances will be serialized as collections according to these rules.

 

Also, one significant feature of POCO serialization is that the parameterless constructor is called when you deserialize an instance of the type. This is not the case for DataContract types or Serializable types. It is the case though for IXmlSerializable types, and ISerializable types do call the special (SerializationInfo information, StreamingContext context) constructor.