我正在尝试将具有域属性的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]。

最佳答案

这是一个非常有趣的问题。目前,答案是“不,不支持”,但是:如果启用AllowParseableTypes,则支持以下内容(在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)模式。但是:我同意最好将它明确地表达出来,而“替代为字符串”是传达这一点的一种好方法。我愿意为问题中的内容添加直接支持,但是今天还不存在,需要进行一些代码更改。可能不是很多,因为该功能的本质已经通过可解析类型存在了!

这是可解析类型方法的示例,该示例今天应该可以使用:

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

07-24 12:32
查看更多