Наследование protobuf-Net показывает базовый класс в файле.proto

Я строю модель времени исполнения для 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

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 и имеет смысл. Для полного описания того, что он видит этот ответ.

Итак: если вы на самом деле намеревались поддерживать наследование в вашей сериализации, ожидается и нормально получить два типа в.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 для номера подтипа, если они хотят. Но каждый номер подтипа не должен конфликтовать с номерами полей того же типа. Опять же, связанный пост более подробно об этом.

Другие вопросы по тегам