Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

DataContractSerializer and immutable types. Deserialising to a known object instance

I have a class that is effectively a object based enum. There is a static set of objects exposed by the class, and everything uses these same instances. eg (Note the private constructor)

[DataContract]
public class FieldType
{
    public static readonly FieldType Default  = new FieldType(1, "Default");
    public static readonly FieldType Name     = new FieldType(2, "Name");
    public static readonly FieldType Etc      = new FieldType(3, "Etc");

    private FieldType(uint id, string name)
    {
        Id = id;
        Name = name;
    }

    [DataMember] public uint   Id   { get; private set; }
    [DataMember] public string Name { get; private set; }
    //snip other properties
}

This works great until I have to serialise across WCF. The DataContractSerializer creates new objects by bypassing the constructor. This results in a valid FieldType object, but it is a new instance that is not one of my static instances. This makes reference comparisons against the known static values fail.

Is there any way to override the serialisation behaviour for a class so that I create the object instance instead of populating an instance supplied to me?

like image 586
GazTheDestroyer Avatar asked Apr 11 '12 11:04

GazTheDestroyer


People also ask

What is datacontractserializer in Salesforce?

Serializes and deserializes an instance of a type into an XML stream or document using a supplied data contract. This class cannot be inherited. The following example code shows a type named Person that is serialized by the DataContractSerializer.

How do you use known type in deserialization?

One way to let the deserialization engine know about a type is by using the KnownTypeAttribute. The attribute cannot be applied to individual data members, only to whole data contract types. The attribute is applied to an outer type that can be a class or a structure.

How to serialize a business object to a datacontract type?

To serialize a Business object marked with [ DataContract] and properties marked with [ DataMember] attributes… Then to deserialize back to your DataContract type, use this logic…

How does deserialization work with circletype and triangletype in companylogo2?

Whenever the outer type CompanyLogo2 is being deserialized, the deserialization engine knows about CircleType and TriangleType and, therefore, is able to find matching types for the "Circle" and "Triangle" data contracts.


1 Answers

I suspect you can do:

[DataContract]
public class FieldType : IObjectReference
{
    object IObjectReference.GetRealObject(StreamingContext ctx)
        switch(Id) {
            case 1: return Default;
            case 2: return Name; // note this is a collision between static/non-static
            case 3: return Etc;
            default: throw new InvalidOperationException();
        }
    }
    public static readonly FieldType Default  = new FieldType(1, "Default");
    // note this is a collision between static/non-static
    public static readonly FieldType Name     = new FieldType(2, "Name");
    public static readonly FieldType Etc      = new FieldType(3, "Etc");

    private FieldType(uint id, string name)
    {
        Id = id;
        Name = name; // note this is a collision between static/non-static
    }

    [DataMember] public uint   Id   { get; private set; }
    // note this is a collision between static/non-static
    [DataMember] public string Name { get; private set; }
    //snip other properties
}

Validated:

public static class Program
{
    static void Main()
    {
        var obj = FieldType.Default;

        using(var ms = new MemoryStream())
        {
            var ser = new DataContractSerializer(typeof (FieldType));
            ser.WriteObject(ms, obj);
            ms.Position = 0;
            var obj2 = ser.ReadObject(ms);

            bool pass = ReferenceEquals(obj, obj2); // true
        }
    }
}

Note, however, that there seems little point serializing the Name if we only use the Id to identify the real object to use.

like image 173
Marc Gravell Avatar answered Sep 18 '22 11:09

Marc Gravell