簡體   English   中英

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

[英]protobuf-net custom serialization and model configuration

因此,我正在為Unity3D編寫一個自定義序列化系統,使用戶可以實現和選擇不同的序列化。 我目前支持BinaryFormatterprotobuf-net 然而; 在此系統中,我有一些持久的自定義序列化規則,希望序列化程序可以很好地與以下各項配合使用:

  1. 類型只有在使用[Serializable]注釋時才可[Serializable]
  2. 具有副作用的屬性不會序列化,只有自動屬性
  3. 公共字段/自動屬性隱式序列化
  4. 僅當對非公共字段/自動屬性應用自定義屬性時,它們才會被序列化(我有多種選擇: [Save][Serialize][SerializeField]

現在,我想使我的protobuf-net模型適應這些規則,因此我不必使用任何protobuf的自定義屬性,例如ProtoContractProtoMember等。

我認為可以做到這一點的方法是,擁有一個可序列化類型的數組,用戶可以將其自定義類型添加到其中(這樣他就不需要這些類型上的ProtoContract)-我可以迭代這些類型並將其添加到我的模特。 對於Foreach類型,我將獲得滿足序列化規則的成員,並將其也添加到模型中。

我想說的另一件事是,假設您有一個帶有孩子BC的抽象類A ,用戶不必顯式添加BC ,他們只需添加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在此階段無法識別的B ,因為尚未將其添加到模型中。因此,我認為有必要先添加類型,然后再添加其成員。 ..

主要問題似乎是該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中,標識非常重要:未嚴格定義標識的結果是,如果有人將AardvarkCount添加到模型中,則可能會抵消所有標識,並破壞現有數據的反序列化。 需要提防的東西。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM