настраиваемая сериализация protobuf-net и конфигурация модели

1

Поэтому я пишу пользовательскую систему сериализации Unity3D, которая позволяет пользователям реализовывать и выбирать различные сериализации. В настоящее время я поддерживаю BinaryFormatter и protobuf-net. Однако; в этой системе у меня есть постоянные пользовательские правила для сериализации, которые я бы хотел, чтобы мои сериализаторы играли хорошо с:

  1. Типы сериализуются, только если они аннотируются с помощью [Serializable]
  2. Свойства с побочными эффектами не сериализованы, только авто
  3. Публичные поля/автоматические свойства сериализуются неявно
  4. Непубличные поля/автоматические свойства сериализуются только тогда, когда к ним применяется пользовательский атрибут (у меня есть множество таких: [Save], [Serialize] и [SerializeField]

Теперь я хотел бы адаптировать мою модель protobuf-net для адаптации к этим правилам, поэтому мне не нужно использовать какие-либо пользовательские атрибуты ProtoContract, ProtoMember как ProtoContract, ProtoMember и т.д.

То, как я полагал, что смогу это сделать, состоит в том, что у него есть набор сериализуемых типов, к которым пользователь будет добавлять свои собственные типы (таким образом, ему не нужен ProtoContract для этих типов) - я бы перебирал эти типы и добавлял их в моя модель. Тип Foreach, я бы получил членов, которые удовлетворяли бы моим правилам сериализации и добавили их в модель.

Еще одна вещь, которую я хотел бы сказать, говорит, что у вас есть абстрактный класс A с детьми B и C, пользователям не нужно явно добавлять B и C, они просто добавляют A и я получаю A children и добавляю их самостоятельно.

Мой вопрос сводится к следующему: вместо того, чтобы пользователи писали это:

[ProtoContract]
[ProtoInclude(1, typeof(Child1))]
[ProtoInclude(2, typeof(Child2))]
public abstract class AbstractBase
{
    public abstract int Num { get; set; }
}

[ProtoContract]
public class Child1 : AbstractBase
{
    [ProtoMember(1)]
    public int x;

    public override int Num { get { return x; } set { x = value; } }
}

[ProtoContract]
public class Child2 : AbstractBase
{
    [ProtoMember(1)]
    public int y;
    [ProtoMember(2)]
    public int z;

    public override int Num { get { return y; } set { y = value; } }
}

Я хотел бы, чтобы они могли написать это:

[Serializble]
public abstract class AbstractBase
{
    public abstract int Num { get; set; }
}

[Serializble]
public class Child1 : AbstractBase
{
    public int x;

    public override int Num { get { return x; } set { x = value; } }
}

[Serializble]
public class Child2 : AbstractBase
{
    public int y;
    public int z;

    public override int Num { get { return y; } set { y = value; } }
}

// ProtobufSerializableTypes.cs
public static Type[] SerializableTypes = new[]
{
    typeof(AbstractBase)
};

Вот что я пробовал:

[TestClass]
public class ProtobufDynamicSerializationTestSuite
{
    private AbstractBase Base { get; set; }
    private Type[] SerializableTypes { get; set; }

    [TestInitialize]
    public void Setup()
    {
        Base = new Child1();
        SerializableTypes = new[]
        {
            typeof(AbstractBase)
        };
    }

    [TestMethod]
    public void ShouldCopyWithCustomConfig()
    {
        var model = TypeModel.Create();

        Func<Type, MetaType> addType = type =>
        {
            log("adding type: {0}", type.Name);
            return model.Add(type, false);
        };

        var hierarchy = new Dictionary<MetaType, List<Type>>();
        for (int i = 0; i < SerializableTypes.Length; i++)
        {
            var type = SerializableTypes[i];
            var meta = addType(type);
            var temp = new List<Type>();
            var children = type.Assembly.GetTypes().Where(t => t.IsSubclassOf(type) && !t.IsAbstract).ToList();
            for(int j = 0; j < children.Count; j++)
            {
                var child = children[j];
                addType(child);
                log("adding subtype {0} with id {1}", child.Name, j + 1);
                meta.AddSubType(j + 1, child);
                temp.Add(child);
            }
            hierarchy[meta] = temp;
        }

        Func<Type, string[]> getMemberNames = x =>
            //SerializationLogic.GetSerializableMembers(x, null) // real logic
                x.GetMembers(BindingFlags.Instance | BindingFlags.DeclaredOnly | BindingFlags.Public) // dummy logic
                             .Where(m => m.MemberType == MemberTypes.Field)
                             .Select(m => m.Name)
                             .ToArray();

        foreach (var entry in hierarchy)
        {
            int id = 1;
            foreach (var type in entry.Value)
            {
                foreach (var member in getMemberNames(type))
                {
                    log("adding member {0} to type {1} with id {2}", member, type.Name, id);
                    entry.Key.Add(id++, member);
                }
            }
        }

        Base.Num = 10;
        var copy = (AbstractBase)model.DeepClone(Base);
        Assert.AreEqual(copy.Num, 10);
    }

    void log(string msg, params object[] args)
    {
        Console.WriteLine(string.Format(msg, args));
    }

    void log(string msg)
    {
        log(msg, new object[0]);
    }
}

Поэтому моя попытка состояла в том, чтобы добавить в модель все необходимые типы, добавить все типы детей в родительские типы и затем перебрать все добавленные типы и добавить в модель соответствующие поля/свойства (соответствующие моим правилам сериализации) из этого типа

Однако это не позволяет:

Test Name:  ShouldCopyWithCustomConfig
Test Outcome:   Failed
Result Message: 
Test method ProtobufTests.ProtobufDynamicSerializationTestSuite.ShouldCopyWithCustomConfig threw exception: 
System.ArgumentException: Unable to determine member: x
Parameter name: memberName
Result StandardOutput:  
adding type: AbstractBase
adding type: Child1
adding subtype Child1 with id 1
adding type: Child2
adding subtype Child2 with id 2
adding member x to type Child1 with id 1

Что я делаю не так? и есть ли лучший способ сделать это?

Благодарю!


Обратите внимание, что изначально у меня не было этого словарного шага, я попытался добавить членов типа сразу после добавления его в модель, но это не удается, если я говорю, типа A и B, A имеет ссылку B, если я пытаюсь добавьте тип A и его члены, я приду по B который protobuf не может идентифицировать на этом этапе, потому что он еще не добавлен в модель... Поэтому я подумал, что сначала необходимо добавить типы, а затем их членов...

  • 0
    Здравствуй; Я автор protobuf-сети; Сегодня я весь день на семинаре Redis, поэтому не могу вскочить, но сделаю это, как только смогу
  • 0
    О, спасибо, что дали мне знать! Я планировал дождаться твоего ответа: я попробую кое-что свое, пока ты меня ударишь
Показать ещё 4 комментария
Теги:
serialization
protobuf-net

1 ответ

1
Лучший ответ

Основная проблема заключается в том, что entry.Key ссылается на базовый тип, но вы пытаетесь описать членов определенных подтипов; вот что я сделал:

        foreach (var entry in hierarchy)
        {
            foreach (var type in entry.Value)
            {
                var meta = model.Add(type, false);
                var members = getMemberNames(type);
                log("adding members {0} to type {1}",
                    string.Join(",", members), type.Name);
                meta.Add(getMemberNames(type));
            }
        }

Я также добавил строгий порядок:

.OrderBy(m => m.Name) // in getMemberNames

а также

.OrderBy(x => x.FullName) // in var children =

чтобы идентификаторы были, по крайней мере, предсказуемыми. Обратите внимание, что в protobuf идентификаторы очень важны: следствие не жесткого определения идентификаторов заключается в том, что если кто-то добавит AardvarkCount к вашей модели, он может компенсировать все идентификаторы и отменить десериализацию существующих данных. Что-то, на что нужно следить.

  • 0
    Спасибо за Ваш ответ! - извините, но кажется, что вы снова добавляете типы в модель? то есть это тот же словарь иерархии, который я получил из моего цикла for выше? .. а что вы имеете в виду с AardvarkCount ? : s
  • 0
    @vexe Add вернет существующий MetaType, если он уже определен; AardvarkCount, я пытаюсь выделить проблему повторяемости идентификаторов в будущем. Если кто-то добавляет / удаляет типы или свойства, способна ли модель десериализовать старые данные? Идентификаторы в protobuf должны быть повторяемыми, чтобы это работало. Если идентификаторы генерируются только в алфавитном порядке, это трудно гарантировать.
Показать ещё 4 комментария

Ещё вопросы

Сообщество Overcoder
Наверх
Меню