C# protobuf net自定义序列化和模型配置
因此,我正在为Unity3D编写一个自定义序列化系统,允许用户实现和选择不同的序列化。我目前支持C# protobuf net自定义序列化和模型配置,c#,serialization,protobuf-net,C#,Serialization,Protobuf Net,因此,我正在为Unity3D编写一个自定义序列化系统,允许用户实现和选择不同的序列化。我目前支持BinaryFormatter和protobuf-net。然而;在这个系统中,我有用于序列化的持久自定义规则,我希望我的序列化程序能够很好地使用这些规则: 仅当使用[serializable] 具有副作用的属性不会序列化,仅自动序列化 公共字段/自动属性被隐式序列化 非公共字段/自动属性只有在对其应用自定义属性时才会序列化(我有多种类型:[Save]、[Serialize]和[SerializeFie
BinaryFormatter
和protobuf-net
。然而;在这个系统中,我有用于序列化的持久自定义规则,我希望我的序列化程序能够很好地使用这些规则:
[serializable]
[Save]
、[Serialize]
和[SerializeField]
ProtoContract
,ProtoMember
等
我想我可以这样做的方法是,拥有一个可序列化类型数组,用户可以将其自定义类型添加到其中(这样他就不需要对这些类型使用ProtoContract)-我将迭代这些类型并将它们添加到我的模型中。对于每个类型,我将获得满足序列化规则的成员,并将它们添加到模型中
另一件事我想说的是,假设你有一个抽象类A
和子类B
和C
,用户不必显式地添加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)
};
我希望他们能够写下:
[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]);
}
}
我做错了什么?还有更好的方法吗
谢谢
请注意,最初我没有这个字典步骤,我尝试在将类型添加到模型后立即添加类型的成员,但如果我说,类型
a
和B
,a
有一个B
引用,那么这失败了,如果我尝试添加类型a
及其成员,我会加入B
,哪个协议在这个阶段无法识别,因为它还没有添加到模型中…所以我认为有必要先添加类型,然后再添加它们的成员…主要问题似乎是条目。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
及
确保ID至少是可预测的。请注意,在protobuf中,ID非常重要:不严格定义ID的后果是,如果有人向您的模型中添加
AardvarkCount
,它可能会抵消所有ID,并破坏现有数据的反序列化。需要注意的是。Hi;我是protobuf的作者-我今天一整天都在Redis工作室,所以我不能跳进去,但我一跳就可以。谢谢你让我知道!我正计划等待你的回答:D你打我的时候,我会自己尝试一些东西back@MarcGravell我还没弄明白。可能需要一些帮助;现在正在偷懒;对不起,我昨天早上5点到晚上10点30分出去了,还有一个fu今天也是!我很困惑;在循环中,entry
是基类,而type
是子类-所以您尝试添加(entry.Key.add
)子类型到基类型的模型的属性…?你能澄清这是否是故意的吗?谢谢你的回答!-抱歉,但你似乎又在向模型添加类型?即,这是我从上面的for循环中得到的同一个层次结构字典吗?。你对AardvarkCount
的意思是什么s@vexeA.如果已经定义了现有的元类型,dd将返回该元类型;根据AardvarkCount,我试图强调ID在将来是可重复的问题。如果有人添加/删除类型或属性,模型是否仍然能够反序列化旧数据?protobuf中的ID需要可重复才能工作。如果ID只生成字母嗯,这很难保证。谢谢你,好先生!-但是,我觉得我所做的是一种过火的行为。有没有更简单的方法?我认为有必要在我将类型添加到模型之后而不是在添加期间添加类型成员(请参阅问题的最后添加部分)-我说的对吗?@vexe这没什么大不了的,但你的方式还可以。会员添加允许“只需使用下一个可用id”用法,子类型添加没有,IIRC,所以最后添加成员可能是最方便的。我的直觉是正确的:D我根本不需要字典-添加类型后,我可以通过model.GetTypes()添加类型
-但我很好奇为什么该方法的返回类型是非泛型的IEnumerable
,而不是IEnumerable
?