【问题标题】:protobuf-net custom serialization and model configurationprotobuf-net 自定义序列化和模型配置
【发布时间】:2014-10-02 12:45:23
【问题描述】:

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

  1. 只有使用[Serializable] 注释的类型才可序列化
  2. 不序列化有副作用的属性,只有自动序列化
  3. 公共字段/自动属性被隐式序列化
  4. 非公共字段/自动属性仅在对其应用自定义属性时进行序列化(我有多种:[Save][Serialize][SerializeField]

现在我想调整我的 protobuf-net 模型以适应这些规则,这样我就不必使用任何 protobufs 自定义属性,例如 ProtoContractProtoMember 等。

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

我想要的另一件事是,假设您有一个抽象类 A 和孩子 BC,用户不必显式添加 BC,他们只需添加 @ 987654334@ 我会得到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

我做错了什么?有没有更好的方法来做到这一点?

谢谢!


请注意,最初我没有此字典步骤,我尝试在将类型的成员添加到模型后立即添加它,但如果我说输入 ABA,则会失败有一个B 引用,如果我尝试添加类型A 及其成员,我会出现B,protobuf 在这个阶段无法识别,因为它还没有添加到模型中......所以我认为有必要先添加类型,然后添加它们的成员......

【问题讨论】:

  • 嗨;我是protobuf-net的作者;我今天整天都在 Redis 研讨会上,所以我不能加入 - 但我会尽快加入
  • 哦,谢谢你告诉我!我本来打算等你的回答 :D 当你回击我时,我会自己尝试一些事情
  • @MarcGravell 还没有弄清楚。可以使用一些帮助;P
  • 现在看;抱歉,我昨天早上 5 点到晚上 10 点 30 分都出去了,今天也是一整天!
  • 我很困惑;在循环中,entry 是基类,但 type 是子类 - 所以您尝试将 (entry.Key.Add) 子类型的属性添加到基类型的模型中...?你能澄清这是否是故意的吗?

标签: c# serialization protobuf-net


【解决方案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,并破坏现有数据的反序列化。需要注意的地方。

【讨论】:

  • 感谢您的回答! - 抱歉,您似乎再次将类型添加到模型中?即,这与我从上面的 for 循环中获得的层次结构字典是否相同? ..你说的AardvarkCount是什么意思? :s
  • @vexe Add 将返回已定义的现有 MetaType;通过 AardvarkCount,我试图强调 id 在未来可重复的问题。如果有人添加/删除类型或属性,模型是否仍然能够反序列化旧数据? protobuf 中的 id 需要可重复才能正常工作。如果 id 仅按字母顺序生成,则很难保证。
  • 谢谢您的好先生! - 然而;我觉得我在做的事情有点矫枉过正。有没有更简单的方法?我认为有必要在将类型添加到模型之后而不是在添加过程中添加类型成员(请参阅问题的最后添加部分) - 我正确吗?
  • @vexe 没什么大不了的,但是您拥有的方式还可以。成员方式添加允许“并且只使用下一个可用的 id”使用,子类型添加不允许,IIRC,所以最后添加成员可能是最方便的。
  • 我的预感是正确的 :D 我根本不需要那个字典——添加类型后,我可以通过model.GetTypes() 添加类型——但我很好奇为什么返回类型该方法是非通用的IEnumerable 而不是IEnumerable&lt;MetaType&gt;
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2013-11-17
相关资源
最近更新 更多