我正在尝试将具有域属性的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.Default
是Serializer.*
方法后面的实例):
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);
}
}