2

I am trying to serialize custom EntityData class into Eyeshot proprietary file format. There is a great article about this (https://devdept.zendesk.com/hc/en-us/articles/360003318873-Eyeshot-Proprietary-File-Format),
it works fine if I serialize base class, however I can't serialize a class that is derived from my base class. Here is a sample, I tried to keep it as small as possible(please read comments along the way):

public class BaseClass
{
    public int Id { get; set; }
    public virtual BaseClassSurrogate ConvertToSurrogate() { return new BaseClassSurrogate(this); }
}
public class BaseClassSurrogate : Surrogate<BaseClass>
{
    public BaseClassSurrogate(BaseClass myBaseClass) : base(myBaseClass) { }
    public int Id { get; set; }
    protected override BaseClass ConvertToObject()
    {
        var baseClass = new BaseClass();
        CopyDataToObject(baseClass);
        return baseClass;
    }
    protected override void CopyDataFromObject(BaseClass obj) { Id = obj.Id; }
    protected override void CopyDataToObject(BaseClass obj) { obj.Id = this.Id; }
    public static implicit operator BaseClass(BaseClassSurrogate surrogate) { return surrogate?.ConvertToObject(); }
    public static implicit operator BaseClassSurrogate(BaseClass source) { return source?.ConvertToSurrogate(); }
}

And my derived class with its surrogate implementation:

public class DerivedClass : BaseClass
{
    public int Number { get; set; }
    public override BaseClassSurrogate ConvertToSurrogate() { return new DerivedClassSurrogate(this); }
}

public class DerivedClassSurrogate : BaseClassSurrogate
{
    public DerivedClassSurrogate(DerivedClass baseClass) : base(baseClass) { }
    public int Number { get; set; }
    protected override BaseClass ConvertToObject()
    {
        var derivedClass= new DerivedClass();
        CopyDataToObject(derivedClass);
        return derivedClass;
    }
    protected override void CopyDataFromObject(BaseClass obj)
    {
        if (obj is DerivedClass derivedClass)
            Number = derivedClass.Number;
        base.CopyDataFromObject(obj);
    }
    protected override void CopyDataToObject(BaseClass obj)
    {
        if (obj is DerivedClass derivedClass)
            derivedClass.Number = Number;
        base.CopyDataToObject(obj);
    }
    //I don't understand do I need to call these in derived class as well?
    //public static implicit operator BaseClass(BaseClassSurrogate surrogate) { return surrogate?.ConvertToObject(); }
    //public static implicit operator BaseClassSurrogate(BaseClass source) { return source?.ConvertToSurrogate(); }
}

And here is FillModel method from FileSerializer class:

protected override void FillModel()
{
    base.FillModel();

    Model.Add(typeof(BaseClass), false)
         .SetSurrogate(typeof(BaseClassSurrogate));

    MetaType mt1 = Model[typeof(BaseClassSurrogate)]
        .Add(1, "Id");

    mt1.SetCallbacks(null, null, "BeforeDeserialize", null); 
    mt1.UseConstructor = false; 

    Model.Add(typeof(DerivedClass), false)
    .SetSurrogate(typeof(DerivedClassSurrogate));

    MetaType mt2 = Model[typeof(DerivedClassSurrogate)]
        .Add(1, "Number");

    mt2.SetCallbacks(null, null, "BeforeDeserialize", null); 
    mt2.UseConstructor = false;
}

This code gives me error:"No suitable conversion operator found for surrogate DerivedClass/DerivedClassSurrogate". Any help would be highly appreciated.

Marc Gravell
  • 1,026,079
  • 266
  • 2,566
  • 2,900
niks
  • 579
  • 1
  • 3
  • 14
  • lol, their "proprietary file format" : is basically protobuf via protobuf-net... sheesh; let me take a look... – Marc Gravell Jul 02 '20 at 13:53
  • You need a custom serialize method for each typeof(Class). I think what is happening is the serialize is adding a type enumeration in the serialized object. Then when you are deserializing the type enumeration is not being found. – jdweng Jul 02 '20 at 13:59
  • OK, here's the thing: I'm the author of the library they're using for this, and I haven't got a clue what they're doing or why... or why they're going the "surrogate" route, when everything they're showing can be done very simply (and safely) via attributes and letting the library worry about the details. You *might* want to try using the `AddSubType` method on the `MetaType` that represents the base, but... without knowing what they're *doing here*, it is hard to be sure. Emphasis: if I'm confused as to what they're doing, and I wrote the library: they're quite possibly doing something weird. – Marc Gravell Jul 02 '20 at 14:01
  • @Marc Gravell Thank you for answer. I believe the Devdept team has a reason why they have implemented it like this. From discussion in comments(see aforementioned link) :"we use the surrogates to keep a clear separation between the Eyeshot objects definition and the one used for the serialization purpose. In this way, we can better handle versioning and overcome some limits about unsupported types in protobuf-net, like object, enum, multi-dimensional array, etc" – niks Jul 03 '20 at 06:15

1 Answers1

1

In FillModel() method you forgot to specify the hierarchy for your custom classes, try in this way:

protected override void FillModel()
{
    base.FillModel();

    Model.Add(typeof(BaseClass), false)
        .AddSubType(1001, typeof(DerivedClass))
        .SetSurrogate(typeof(BaseClassSurrogate));
    
    Model[typeof(BaseClassSurrogate)]
        .AddSubType(1001, typeof(DerivedClassSurrogate))
        .Add(1, "Id")
        .SetCallbacks(null, null, "BeforeDeserialize", null)
        .UseConstructor = false; 
    
    Model[typeof(DerivedClassSurrogate)]
        .Add(1, "Number")
        .UseConstructor = false;
}
ilCosmico
  • 1,319
  • 15
  • 26
  • Yes!! It works now! Thank you very much! I have one question though - does this mean that `public static implicit operator EntitySurrogate(Entity source)` and `public static implicit operator Entity(EntitySurrogate surrogate)` have to be defined only for base class that inherits directly from `Surrogate`? – niks Jul 03 '20 at 11:15
  • Yes, that's so. It's also written in the article you mentioned *...those static methods must be created only for classes that derive from the Surrogate base class* – ilCosmico Jul 03 '20 at 11:48
  • Yes, you are right! I should be more careful next time. Thank you for answering! – niks Jul 03 '20 at 12:00