简体   繁体   English

protobuf-net定制序列化和模型配置

[英]protobuf-net custom serialization and model configuration

So I'm writing a custom serialization system for Unity3D that lets users implement and choose different serializes. 因此,我正在为Unity3D编写一个自定义序列化系统,使用户可以实现和选择不同的序列化。 I currently support BinaryFormatter and protobuf-net . 我目前支持BinaryFormatterprotobuf-net However; 然而; in this system I have persistent custom rules for serialization that I'd like my serializers to play nice with: 在此系统中,我有一些持久的自定义序列化规则,希望序列化程序可以很好地与以下各项配合使用:

  1. Types are serializable only if they're annotated with [Serializable] 类型只有在使用[Serializable]注释时才可[Serializable]
  2. Properties with side effects are not serialized, only the auto ones 具有副作用的属性不会序列化,只有自动属性
  3. Public fields/auto-properties are serialized implicitly 公共字段/自动属性隐式序列化
  4. Non-public fields/auto-properties are serialized only when a custom attribute is applied on them (I have a variety of those: [Save] , [Serialize] and [SerializeField] 仅当对非公共字段/自动属性应用自定义属性时,它们才会被序列化(我有多种选择: [Save][Serialize][SerializeField]

Now I'd like to adapt my protobuf-net model to adapt to these rules so I don't have to use any of protobufs custom attributes like ProtoContract , ProtoMember etc. 现在,我想使我的protobuf-net模型适应这些规则,因此我不必使用任何protobuf的自定义属性,例如ProtoContractProtoMember等。

The way I figured I could do this, is by having an array of serializable types that the user would add his custom types to (that way he doesn't need ProtoContract on those types) - I would iterate on those types and add them to my model. 我认为可以做到这一点的方法是,拥有一个可序列化类型的数组,用户可以将其自定义类型添加到其中(这样他就不需要这些类型上的ProtoContract)-我可以迭代这些类型并将其添加到我的模特。 Foreach type, I would get the members that would satisfy my serialization rules, and add those to the model too. 对于Foreach类型,我将获得满足序列化规则的成员,并将其也添加到模型中。

Another thing I'd like, is say you have an abstract class A with children B and C , users don't have to explicitly add B and C , they just add A and I would get A 's children and add them myself. 我想说的另一件事是,假设您有一个带有孩子BC的抽象类A ,用户不必显式添加BC ,他们只需添加A ,我就可以得到A的孩子并自己添加它们。

My question boils down to: Instead of users having to write this: 我的问题归结为:无需用户编写以下代码:

[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; } }
}

I would like them to be able to write this: 我希望他们能够这样写:

[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)
};

Here's what I tried: 这是我尝试过的:

[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]);
    }
}

So my attempt was to add all the necessary types to the model, add all children types to parent types, and then iterate on all added types and add to the model the appropriate fields/properties (that correspond to my serialization rules) from that type 所以我的尝试是将所有必需的类型添加到模型,将所有子类型添加到父类型,然后迭代所有添加的类型,并将该类型的适当字段/属性(对应于我的序列化规则)添加到模型中

This is failing however to: 但是,这失败了:

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

What am I doing wrong? 我究竟做错了什么? and is there a better way to do this? 有没有更好的方法可以做到这一点?

Thanks! 谢谢!


Note that initially I didn't have this dictionary step, I tried to add a type's members right after adding it to the model, but this fails if I have say, type A and B , A has a B reference, if I try to add the type A and its members, I will come along B which protobuf is unable to identify at this stage, because it's not added to the model yet... So I thought it was necessary to add the types first, then their members... 请注意,最初我没有这个字典步骤,我尝试在将类型添加到模型后立即添加类型的成员,但是如果我说类型AB ,则失败,如果我尝试使用A则引用B添加类型A及其成员,我将介绍protobuf在此阶段无法识别的B ,因为尚未将其添加到模型中。因此,我认为有必要先添加类型,然后再添加其成员。 ..

The main issue seems to be that entry.Key refers to the base-type, but you are trying to describe members of the specific sub-types; 主要问题似乎是该entry.Key是指基类型,但是您试图描述特定子类型的成员; here's what I did: 这是我所做的:

        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));
            }
        }

I also added some strict ordering: 我还添加了一些严格的顺序:

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

and

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

to ensure that the ids are at least predictable. 以确保ID至少可预测。 Note that in protobuf, the ids are very important: a consequence of not rigidly defining the ids is that if someone adds AardvarkCount to your model, it could offset all the ids, and break deserialization of existing data. 请注意,在protobuf中,标识非常重要:未严格定义标识的结果是,如果有人将AardvarkCount添加到模型中,则可能会抵消所有标识,并破坏现有数据的反序列化。 Something to watch out for. 需要提防的东西。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM