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

时间:2014-10-02 12:45:24

标签: c# serialization protobuf-net

因此,我正在为Unity3D编写自定义序列化系统,以便用户实现和选择不同的序列化。我目前支持BinaryFormatterprotobuf-net。然而;在这个系统中,我有序列化的持久自定义规则,我希望我的序列化程序可以很好地使用:

  1. 类型只有在[Serializable]
  2. 注释后才可序列化
  3. 带有副作用的属性未序列化,只有自动化
  4. 隐式序列化公共字段/自动属性
  5. 非公开字段/自动属性仅在对其应用自定义属性时序列化(我有各种各样的字段:[Save][Serialize][SerializeField]
  6. 现在我想调整我的protobuf-net模型以适应这些规则,因此我不必使用任何protobufs自定义属性,如ProtoContractProtoMember

    我认为我可以做到这一点的方法是,有一个可序列化类型的数组,用户可以添加他的自定义类型(这样他就不需要那些类型的ProtoContract) - 我会迭代这些键入并将它们添加到我的模型中。 Foreach类型,我会得到满足我的序列化规则的成员,并将它们添加到模型中。

    我还想说的另一件事就是说你有一个抽象的课程A,其中有孩子BC,用户不必明确添加{{ 1}}和B,他们只需添加C,我就会得到A个孩子并自行添加。

    我的问题归结为:而不是用户必须写这个:

    A

    我希望他们能够写下这个:

    [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 ,则会失败和AB有一个A引用,如果我尝试添加类型B及其成员,我会来A protobuf无法在这个阶段识别,因为它尚未添加到模型中......所以我认为有必要先添加类型,然后再添加其成员......

1 个答案:

答案 0 :(得分: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 =

确保id至少是可预测的。注意,在protobuf中,id非常重要:不严格定义id的结果是,如果有人将AardvarkCount添加到模型中,它可以抵消所有id,并打破现有数据的反序列化。需要注意的事项。