David Hayes
David Hayes

Reputation: 7512

Is it possible to serialize/deserialize immutable types with protobuf-net on Windows Phone 7/8?

Is it possible to serialize/deserialize types with protobuf-net on Windows Phone 7/8?
I've tried the code below, it seems Constructor skipping isn't supported (i.e. UseConstructor = false) so I created a parameterless constructor but the deserialization fails with "Attempt to access the method failed: Wp7Tests.ImmutablePoint.set_X(System.Double)"

public class ImmutablePoint
{
    public double X { get; private set; }
    public double Y { get; private set; }
    public ImmutablePoint() {}

    public ImmutablePoint(double x, double y)
    {
        X = x;
        Y = y;
    }
}
public sub Test()
{
        ImmutablePoint pt = new ImmutablePoint(1, 2);
        var model = TypeModel.Create();
        var ptType = model.Add(typeof(ImmutablePoint), false);
        ptType.AddField(1, "X");
        ptType.AddField(2, "Y");
        IsolatedStorageFile store = IsolatedStorageFile.GetUserStoreForApplication();

        using (var stream1 = new IsolatedStorageFileStream("test.bin", FileMode.Create, store))
        {
            try
            {
                model.Serialize(stream1, pt);
            }
            catch (Exception e)
            {                    
                Debugger.Break();
            }
        }
        const double EPSILON = 0.001;
        using (var stream1 = new IsolatedStorageFileStream("test.bin", FileMode.Open, store))
        {
            try
            {
                ImmutablePoint ptDeSer = (ImmutablePoint) model.Deserialize(stream1,null, typeof(ImmutablePoint));
                Debug.Assert(Math.Abs(pt.X - ptDeSer.X) < EPSILON);
                Debug.Assert(Math.Abs(pt.Y - ptDeSer.Y) < EPSILON);
            }
            catch (Exception e)
            {
                Debugger.Break();
            }
        }
}

Upvotes: 3

Views: 497

Answers (2)

Marc Gravell
Marc Gravell

Reputation: 1063393

You can use a surrogate; here I'm decorating it with attributes, but it can be configured manually too:

[ProtoContract]
public class MutablePoint {
    [ProtoMember(1)] public double X { get; set; }
    [ProtoMember(2)] public double Y { get; set; }

    public static implicit operator MutablePoint(ImmutablePoint value) {
        return value == null ? null : new MutablePoint {X=value.X, Y=value.Y};
    }
    public static implicit operator ImmutablePoint(MutablePoint value) {
        return value == null ? null : new ImmutablePoint(value.X, value.Y);
    }
}

and then tell the model to use this whenever it sees ImmutablePoint:

var model = TypeModel.Create();
model.Add(typeof(MutablePoint), true);
model.Add(typeof(ImmutablePoint), false).SetSurrogate(typeof(MutablePoint));

The serializer will use the operators to switch between them as necessary. The serializer will use either implicit or explicit custom conversion operators.

EDIT: Deserialize like this

ImmutablePoint ptDeSer = (ImmutablePoint)model.Deserialize(stream1, null, typeof(ImmutablePoint));

Upvotes: 8

calum
calum

Reputation: 1580

This isn't possible on Windows Phone. The deserializer cannot access the setters and using reflection to access private members results in a MemberAccessException on Windows Phone. The only way I can think of getting something like this to work would be by creating a wrapper for serialization which can then call the constructor of your ImmutablePoint. It's all a bit messy though as the wrapper class will need public read-write properties for X and Y.

You could also look at using the InternalsVisibleTo attribute to allow the serializer to access the properties (assuming you change the setters from private to internal). Still messy though...

Upvotes: 2

Related Questions