我正在使用反射在运行时为protobuf-net构建运行时模型,而不注释我需要序列化的类。
我需要序列化的一些类使用继承,当然我想要基类的所有属性。
protobuf-net默认不对爬网树进行爬网,因此您需要告诉它有关基类的信息。所以我写了一小段代码来做到这一点:
public class InheritanceTest
{
public static string CreateProto()
{
var model = ProtoBuf.Meta.RuntimeTypeModel.Default;
var type = typeof(SubClass);
if (null != type.BaseType && type.BaseType != typeof(Object))
{
var hierarchy = new List<Type> { type };
var baseType = type.BaseType;
while (null != baseType)
{
if (baseType != typeof(Object))
{
hierarchy.Add(baseType);
}
baseType = baseType.BaseType;
}
hierarchy.Reverse();
var metaType = model.Add(hierarchy.First(), true);
for (int i = 1; i < hierarchy.Count; i++)
{
model.Add(hierarchy[i], true);
metaType = metaType.AddSubType(i, hierarchy[i]);
}
}
else
{
model.Add(type, true);
}
var properties = type.GetProperties(BindingFlags.Public | BindingFlags.Instance).OrderBy(p => p.Name);
var tagNumber = 1;
foreach (var propertyInfo in properties)
{
model[type].Add(tagNumber, propertyInfo.Name);
tagNumber++;
}
var schema = model.GetSchema(type, ProtoSyntax.Proto3);
return schema;
}
}
public class BaseClass
{
public string StringPropOnBaseClass { get; set; }
}
public class SubClass : BaseClass
{
public string StringPropOnSubClass { get; set; }
}
生成一个.proto文件,如下所示:
syntax = "proto3";
package ProtoBufferSerializerTest;
message BaseClass {
// the following represent sub-types; at most 1 should have a value
optional SubClass SubClass = 1;
}
message SubClass {
string StringPropOnBaseClass = 1;
string StringPropOnSubClass = 2;
}
为什么BaseClass包含在.proto文件中?没有理由为什么这需要渗透到公共线路格式。
有没有办法告诉运行时模型不要在.proto flie中包含它?
BR
答案 0 :(得分:1)
因为你说过了吗?
如果我们将该代码更改为:
Console.WriteLine("Adding: " + hierarchy.First().Name);
var metaType = model.Add(hierarchy.First(), true);
for (int i = 1; i < hierarchy.Count; i++)
{
Console.WriteLine("Adding: " + hierarchy[i].Name);
model.Add(hierarchy[i], true);
Console.WriteLine("Adding as sub type " + i + " to " + metaType.Type.Name);
metaType = metaType.AddSubType(i, hierarchy[i]);
}
我们得到:
Adding: BaseClass
Adding: SubClass
Adding as sub type 1 to BaseClass
因此,您明确将BaseClass
添加为合约类型,并使用AddSubType
在模型中明确地将它们连接起来。 protobuf格式本身(Google规范)不处理继承,所以protobuf-net确实需要在其中工作,所以它通过可选的子对象模拟继承,从根开始 - 因为这是允许您从BaseClass
可靠地反序列化并使其有意义的唯一方法。有关它的作用的完整描述see this answer。
所以:如果实际上打算在序列化中支持继承,那么在.proto中获得两种类型是可取的并且是正常的。如果您不打算支持继承:*不要使用AddSubType
。您只需将基本类型* 直接添加到SubClass
public class InheritanceTest
{
static void Main()
{
Console.WriteLine(CreateProto());
var obj = new SubClass {
StringPropOnBaseClass = "abc",
StringPropOnSubClass = "def" };
var clone = Serializer.DeepClone(obj);
Console.WriteLine(clone.StringPropOnBaseClass);
Console.WriteLine(clone.StringPropOnSubClass);
}
public static string CreateProto()
{
var model = ProtoBuf.Meta.RuntimeTypeModel.Default;
var metaType = model.Add(typeof(SubClass), false);
metaType.AddField(1, "StringPropOnSubClass");
metaType.AddField(2, "StringPropOnBaseClass");
var schema =model.GetSchema(typeof(SubClass), ProtoSyntax.Proto3);
return schema;
}
}
输出:
syntax = "proto3";
message SubClass {
string StringPropOnSubClass = 1;
string StringPropOnBaseClass = 2;
}
和
abc
def
顺便提一下,您在原始代码中为子类型分配数字的方法显示出对哪些数字有效或可取的误解。字段编号不需要在层次结构中的不同级别之间 - - 因此在5级继承树中,如果需要,他们都可以使用1
作为子类型编号。但是每个子类型编号 不需要与同一类型的字段编号冲突。再次,链接的帖子详细介绍了这一点。