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 ISerializabe to take things into your own hands (assuming IXmlSerializable wasn’t used).
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 ASP.NET Web services (ASMX) types over to the DataContractSerializer without sprinkling a bunch of new attributes on them.
Now, with .NET 3.5 SP1, you can serialize the following type with DataContractSerializer:
public class Person
{
public Person() { this.Id = Guid.NewGuid().ToString(); }
private string Id;
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.
Now you can simply use POCO types in your WCF service contracts and you don’t have to worry about changing the serializer back to XmlSerializer using [XmlSerializerFormat]. In other words, the following service contract works with the above type as-is in .NET 3.5 SP1:
[ServiceContract]
public interface IMarriageService
{
[OperationContract]
void MarryPeople(Person p1, Person p2);
}
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.Name = “Aaron”;
p.Spouse = new Person();
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“>
<Name>Aaron</Name>
<Spouse>
<Name>Monica</Name>
<Spouse i:nil=“true“/>
</Spouse>
</Person>
When you use this technique, you have to 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. Or if you apply the [Serializable] attribute, it will fall back to the standard [Serializable] mapping as well. For example, suppose I annotate the Person type with [DataContract]:
[DataContract]
public class Person
{
public Person() { this.Id = Guid.NewGuid().ToString(); }
private string Id;
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], DataContractSerializer no longer treated it like a POCO type.
To summarize, DataContractSerializer provides several different mechanisms for defining the serialization mapping:
1. Simply rely on the public interface (POCO types) with and take the default XML mapping
2. Use [Serializable] to only include fields
3. Use [DataContract] and [DataMember] and apply some basic customization
4. Use IXmlSerializable or ISerializable for advanced mapping customization
I was actually quite 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) back then but got shot down for that very reason. But despite whatever principle it may violate, I like it, because it makes it simpler for folks to get up and running with WCF and it provides an easy migration path for your ASMX types.