protobuf-net use a string type as surrogate

Viewed 692

I am trying to serialize into protobuf format a C# class that has properties on it that are domain specific types. But these types should be handled as value types.

I am using protobuf.net.

An example is this

[ProtoContract]
public class TestClass
{
    [ProtoMember(1)]
    public string StringProperty { get; set; }

    [ProtoMember(2)]
    public DomainTypeToBeHandldedAsString DomainTypeToBeHandldedAsString { get; set; }
}

public class DomainTypeToBeHandldedAsString
{
    public string Value { get; set; }

    public static implicit operator string(DomainTypeToBeHandldedAsString domainType)
    {
        return domainType?.Value;
    }

    public static implicit operator DomainTypeToBeHandldedAsString(string s)
    {
        return new DomainTypeToBeHandldedAsString {Value = s};
    }
}

In the serialized message I do not want people to care about DomainTypeToBeHandldedAsString. I just want them to see a string and send a string.

So I tried doing this:

var model = ProtoBuf.Meta.RuntimeTypeModel.Default;
model.Add(typeof(DomainTypeToBeHandldedAsString), false).SetSurrogate(typeof(string));

But this fails with this exception:

System.ArgumentException: 'Repeated data (a list, collection, etc) has inbuilt behaviour and cannot be used as a surrogate'

Is there a way to specify a custom serializer for such a class? And it should be said that there are also domain types that should be treated as int or other value types. So not just string.

Thanks

EDIT

I tried this in addition:

[ProtoContract]
public class TestClass
{
    [ProtoMember(1)]
    public string StringProperty { get; set; }

    public DomainTypeToBeHandldedAsInt DomainTypeToBeHandldedAsInt { get; set; }
}

[ProtoContract]
public class DomainTypeToBeHandldedAsInt : IConvertible
{
    [ProtoMember(1)]
    public int Value { get; set; }

    public static implicit operator int(DomainTypeToBeHandldedAsInt domainType)
    {
        return domainType?.Value ?? 0;
    }

    public static implicit operator DomainTypeToBeHandldedAsInt(int s)
    {
        return new DomainTypeToBeHandldedAsInt { Value = s };
    }

    public int ToInt32(IFormatProvider provider)
    {
        return Value;
    }

    //All the rest throw a NotImplementedException
}

I then added the type to the RunTimeModel like this:

var model = ProtoBuf.Meta.RuntimeTypeModel.Default;
model[typeof(TestClass)].Add(2, "DomainTypeToBeHandldedAsInt", typeof(Int32), null);

And that resulted in a .proto file like this:

syntax = "proto3";
package ProtoBufferSerializerTest;

message TestClass {
   string StringProperty = 1;
   repeated int32 DomainTypeToBeHandldedAsInt = 2 [packed = false];
}

But I do not want this to be repeated and I don't need the [packed = false] either.

1 Answers
Related