Поэтому я пишу пользовательскую систему сериализации Unity3D, которая позволяет пользователям реализовывать и выбирать различные сериализации. В настоящее время я поддерживаю BinaryFormatter
и protobuf-net
. Однако; в этой системе у меня есть постоянные пользовательские правила для сериализации, которые я бы хотел, чтобы мои сериализаторы играли хорошо с:
[Serializable]
[Save]
, [Serialize]
и [SerializeField]
Теперь я хотел бы адаптировать мою модель protobuf-net для адаптации к этим правилам, поэтому мне не нужно использовать какие-либо пользовательские атрибуты ProtoContract
, ProtoMember
как ProtoContract
, ProtoMember
и т.д.
То, как я полагал, что смогу это сделать, состоит в том, что у него есть набор сериализуемых типов, к которым пользователь будет добавлять свои собственные типы (таким образом, ему не нужен ProtoContract для этих типов) - я бы перебирал эти типы и добавлял их в моя модель. Тип Foreach, я бы получил членов, которые удовлетворяли бы моим правилам сериализации и добавили их в модель.
Еще одна вещь, которую я хотел бы сказать, говорит, что у вас есть абстрактный класс A
с детьми B
и C
, пользователям не нужно явно добавлять B
и C
, они просто добавляют A
и я получаю A
children и добавляю их самостоятельно.
Мой вопрос сводится к следующему: вместо того, чтобы пользователи писали это:
[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
Что я делаю не так? и есть ли лучший способ сделать это?
Благодарю!
Обратите внимание, что изначально у меня не было этого словарного шага, я попытался добавить членов типа сразу после добавления его в модель, но это не удается, если я говорю, типа A
и B
, A
имеет ссылку B
, если я пытаюсь добавьте тип A
и его члены, я приду по B
который protobuf не может идентифицировать на этом этапе, потому что он еще не добавлен в модель... Поэтому я подумал, что сначала необходимо добавить типы, а затем их членов...
Основная проблема заключается в том, что 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 =
чтобы идентификаторы были, по крайней мере, предсказуемыми. Обратите внимание, что в protobuf идентификаторы очень важны: следствие не жесткого определения идентификаторов заключается в том, что если кто-то добавит AardvarkCount
к вашей модели, он может компенсировать все идентификаторы и отменить десериализацию существующих данных. Что-то, на что нужно следить.
AardvarkCount
? : s