Reputation: 7512
If you have two (or more) classes in an ineritance chain (GeoCoordinate inherits from PointF2D in this case) how do you use surrogates correctly to allow serialization of either type?
As an example, I have these two surrogate classes
public class SerializablePointF2D
{
[ProtoMember(1)]
public double[] Values { get; set; }
public static implicit operator SerializablePointF2D(PointF2D value)
{
return value == null ? null : new SerializablePointF2D {Values = value.ToArrayCopy()} ;
}`enter code here`
public static implicit operator PointF2D(SerializablePointF2D value)
{
return value == null ? null : new PointF2D(value.Values);
}
}
[ProtoContract]
public class SerializableGeoCoordinate {
[ProtoMember(1)]
public double[] Values { get; set; }
public static implicit operator SerializableGeoCoordinate(GeoCoordinate value)
{
return value == null ? null : new SerializableGeoCoordinate { Values = value.ToArrayCopy() };
}
public static implicit operator GeoCoordinate(SerializableGeoCoordinate value)
{
return value == null ? null : new GeoCoordinate(value.Values);
}
}
And this code setting up the model
var model = TypeModel.Create();
//GeoCoordinate
model.Add(typeof(PrimitiveSimpleF2D), false).AddSubType(1, typeof(PointF2D));
model.Add(typeof(PointF2D), false).AddSubType(4, typeof(GeoCoordinate)).SetSurrogate(typeof(SerializablePointF2D));
model.Add(typeof(GeoCoordinate), false).SetSurrogate(typeof(SerializableGeoCoordinate));
When I attempt to serialize this it gets serialized as a PointF2D rather than a GeoCoordinate. I've tried every combination of ordering I can think of EDIT: Based on Marc's code below I tried
[ProtoContract]
public class SerializablePointF2D
{
[ProtoMember(1)]
public double[] Values { get; set; }
public static implicit operator SerializablePointF2D(PointF2D value)
{
if (value == null) return null;
var geoCoordinate = value as GeoCoordinate;
if (geoCoordinate != null) return new SerializableGeoCoordinate
{
Values = geoCoordinate.ToArrayCopy(),
};
return new SerializablePointF2D {Values = value.ToArrayCopy()};
}
public static implicit operator PointF2D(SerializablePointF2D value)
{
return value == null ? null : new PointF2D(value.Values);
}
}
[ProtoContract]
public class SerializableGeoCoordinate:SerializablePointF2D
{
}
Which I thought looked right. It failed with
System.InvalidOperationException : Unexpected sub-type: OsmSharp.Serialization.OsmSharpSerializer+SerializableGeoCoordinate
Upvotes: 1
Views: 761
Reputation: 7512
Got it based on both the answers (Thanks!!!)
[ProtoContract]
[ProtoInclude(2, typeof(SerializableGeoCoordinate))]
public class SerializablePointF2D
{
[ProtoMember(1)]
public double[] Values { get; set; }
public static implicit operator SerializablePointF2D(PointF2D value)
{
if (value == null) return null;
var geoCoordinate = value as GeoCoordinate;
if (geoCoordinate != null) return new SerializableGeoCoordinate
{
Values = geoCoordinate.ToArrayCopy(),
};
return new SerializablePointF2D {Values = value.ToArrayCopy()};
}
public static implicit operator PointF2D(SerializablePointF2D value)
{
if (value == null) return null;
var geoCoordinate = value as SerializableGeoCoordinate;
if (geoCoordinate != null)
{
return new GeoCoordinate(geoCoordinate.Values);
}
return new PointF2D (value.Values );
}
}
[ProtoContract]
public class SerializableGeoCoordinate:SerializablePointF2D
{
}
Upvotes: 2
Reputation: 1062780
So: you have 3 non-serializable types, and you want to add surrogates for them? Perhaps the trick here is to realise that protobuf-net always goes to the root of any inheritance model - so if a surrogate is declared there: it wins; surrogates take over the serialization/deserialization process completely. The following works by making the surrogates mimic the inheritance of the originals - any use?
using ProtoBuf;
using ProtoBuf.Meta;
using System;
public class A
{
// we'll use this to detect how it was constructed
public bool ViaOperator { get; set; }
public int X { get; set; }
}
public class B : A
{
public int Y { get; set; }
}
public class C : B
{
public int Z { get; set; }
}
[ProtoContract, ProtoInclude(1, typeof(BSer))]
public class ASer
{
[ProtoMember(2)] public int X { get; set; }
protected virtual A ToA()
{
return new A { X = X };
}
public static implicit operator A(ASer value)
{
if (value == null) return null;
var a = value.ToA();
a.ViaOperator = true;
return a;
}
public static implicit operator ASer(A value)
{
if (value == null) return null;
var c = value as C;
if(c != null) return new CSer {
X = c.X, Y = c.Y, Z = c.Z};
var b = value as B;
if(b != null) return new BSer {
X = b.X, Y = b.Y };
return new ASer { X = value.X };
}
}
[ProtoContract, ProtoInclude(1, typeof(CSer))]
public class BSer : ASer
{
[ProtoMember(2)] public int Y { get; set; }
protected override A ToA()
{
return new B { X = X, Y = Y };
}
}
[ProtoContract]
public class CSer : BSer
{
[ProtoMember(2)] public int Z { get; set; }
protected override A ToA()
{
return new C { X = X, Y = Y, Z = Z };
}
}
static class Program
{
static void Main()
{
var model = TypeModel.Create();
model.Add(typeof(A), false).AddSubType(2, typeof(B)).SetSurrogate(typeof(ASer));
model[typeof(B)].AddSubType(2, typeof(C));
A obj = new B { X = 1, Y = 2 };
var clone = (B)model.DeepClone(obj);
Console.WriteLine("{0}, {1}, {2}", clone.X, clone.Y, clone.ViaOperator);
}
}
Upvotes: 1
Reputation: 18359
Have you tried using ProtoInclude
in PointF2D
to include GeoCoordinate
? Like this:
[Serializable,
ProtoContract(ImplicitFields = ImplicitFields.AllFields, ImplicitFirstTag = 1),
ProtoInclude(20, "GeoCoordinate")]
public class PointF2D
{
...etc...
}
That should force it to use the surrogate SerializableGeoCoordinate
.
Upvotes: 1