2017-07-28 80 views
1

我在运行时使用反射为我构建protobuf-net的运行时模型,而无需注释需要序列化的类。protobuf-net继承在.proto文件中显示基类

我需要序列化的一些类使用继承,当然我想要基类的所有属性。

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

回答

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。你可以只从基本类型添加你需要成员*直接到​​:

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作为子类型号。但是每个子类型号的确需要的字段号不冲突。链接的帖子再一次详细介绍了这一点。

+0

感谢您的回答。我最初要求运行时模型在添加类型时应用默认行为,但这意味着我的.proto文件中没有获得任何基类属性。答案是告诉它不要应用默认行为。然后我根本不必担心基类。非常感谢! –

+0

@JayPete如果你的类型没有装饰属性(protocontract,datacontract等),那么默认行为不存在*反正* –

+0

很高兴知道!谢谢! –