protobuf-net使用字符串类型作为代理

时间:2017-07-26 09:53:14

标签: c# serialization protocol-buffers protobuf-net

我正在尝试将一个C#类序列化为protobuf格式,该类具有特定于域的类型的属性。但是这些类型应该作为值类型处理。

我正在使用protobuf.net。

这是一个例子

[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};
    }
}

在序列化消息中,我不希望人们关心DomainTypeToBeHandldedAsString。我只是希望他们看到一个字符串并发送一个字符串。

所以我试着这样做:

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

但是这个例外失败了:

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

有没有办法为这样的类指定自定义序列化程序?应该说,还有一些域类型应该被视为int或其他值类型。所以不仅仅是字符串。

由于

修改

我另外尝试了这个:

[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
}

然后我将类型添加到RunTimeModel中:

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

这导致了一个像这样的.proto文件:

syntax = "proto3";
package ProtoBufferSerializerTest;

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

但我不想重复这一点,我也不需要[packed = false]。

1 个答案:

答案 0 :(得分:2)

这是一个非常有趣的问题。目前,答案是"不,不支持",但是:支持以下 如果您启用{ {1}}(在RuntimeTypeModel实例上 - RuntimeTypeModel.DefaultSerializer.*方法背后的实例):

public class DomainTypeToBeHandldedAsString
{
    public string Value { get; set; }
    public override string ToString() => Value;
    public static DomainTypeToBeHandldedAsString Parse(string s)
        => new DomainTypeToBeHandldedAsString { Value = s };
}

基本上,它会查找public static {Type} Parse(string)模式。但是:我同意明确表达这一点更好,而且#34;代理作为字符串"传达这个是一种很好的方式。我愿意为你在问题中的内容添加直接支持,但是现在还没有,需要进行一些代码更改。可能不是很多,因为该功能的 essentials 已经通过parseable-types存在!

这是一个可解析类型的方法,在一个应该在今天工作的例子中工作:

using ProtoBuf;
using ProtoBuf.Meta;

public class DomainTypeToBeHandldedAsString
{
    public string Value { get; set; }
    public override string ToString() => Value;
    public static DomainTypeToBeHandldedAsString Parse(string s)
        => new DomainTypeToBeHandldedAsString { Value = s };
}
[ProtoContract]
public class Bar
{
    [ProtoMember(1)]
    public DomainTypeToBeHandldedAsString A { get; set; }
}
class Program
{
    static void Main()
    {
        RuntimeTypeModel.Default.AllowParseableTypes = true;
        var obj = new Bar { A = new DomainTypeToBeHandldedAsString { Value = "abcdef" } };
        var clone = Serializer.DeepClone(obj);
        System.Console.WriteLine(clone.A.Value);
    }
}