我使用JSON.Net来保存从接口继承的对象集合。我发现我可以将TypeNameHandling设置为
JsonSerializerSettings.TypeNameHandling = TypeNameHandling.Auto;
我可以获得注入到发射的json中的对象的完全限定类型名称,可以在序列化中使用它来构造正确的对象。
"$type": "Newtonsoft.Json.Samples.Hotel, Newtonsoft.Json.Tests"
然而,我非常满意我的resharper重构,这种映射会在代码版本之间快速破解。
我想要的不是存储完全限定的类型名称,而是将每个类分配一个Guid作为类型ID,它在重构时永远不会改变,并使用它来映射回反序列化代码。
这是某人以前尝试过的事情,如果是这样的话怎么去做?
答案 0 :(得分:2)
修改强>
我们现在有一个开源项目来帮助迁移。
https://github.com/Weingartner/Migrations.Json.Net
原始回答
这是我写的解决我的问题的JsonConverter。我所做的是使用 Guid 属性(通常用于com interop)标记我的序列化。然后我提供了一个基于guid的工厂。该工厂基于ninject DI框架。
关键的代码行在ReadJson方法
中var document = _Kernel.Get<IWeinCadDocument>(typeId.ToString());
这里我根据Guid字符串创建了一个正确类型的文档实例。使用以下功能预先注册类型。
public void BindDocumentTypes(params Type[] types)
{
Debug.Assert
(types.All(p => typeof (IWeinCadDocument).IsAssignableFrom(p)));
foreach (var documentType in types)
{
Kernel.Bind<IWeinCadDocument>()
.To(documentType)
.Named(documentType.GUID.ToString());
}
}
可以通过以下方式使用它们的Guid注册一组类型。
BindDocumentTypes(
typeof (ADocument), typeof (BDocument), typeof (CDocument)
);
为了确保Guid不会在软件版本之间发生变化甚至编译,我们会使用属性强制执行Guid。
[Guid("4882176A-751A-4153-928A-915BEA87FAB3")]
public class ADocument : WeinCadDocumentBase<ADocument>
{
public ADocument( IWeinCadDocumentStorage storage )
: base(storage)
{
}
public override object PersistentData
{
get
{
return new DocumentData(10, 20);
}
}
}
完整的JSonConverter在下面。
public class WeinCadDocumentConverter : JsonConverter
{
private readonly IKernel _Kernel;
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
var document = value as IWeinCadDocument;
Debug.Assert(document != null, "document != null");
AssertThatWeHaveACustomGuidSet(value);
writer.WriteStartObject();
writer.WritePropertyName("InstanceId");
writer.WriteValue(document.InstanceId);
writer.WritePropertyName("TypeId");
writer.WriteValue(document.GetType().GUID);
writer.WritePropertyName("Name");
writer.WriteValue(document.Name);
writer.WritePropertyName("Data");
serializer.Serialize(writer, document.PersistentData);
writer.WriteEndObject();
}
/// <summary>
/// The object need a custom GuidAttribute to be set on the class otherwise the
/// GUID may change between versions of the code or even runs of the applications.
/// This Guid is used for identifying types from the document store and calling
/// the correct factory.
/// </summary>
/// <param name="value"></param>
private static void AssertThatWeHaveACustomGuidSet(object value)
{
var attr = System.Attribute.GetCustomAttributes(value.GetType())
.Where(a => a is GuidAttribute)
.ToList();
if (attr.Count == 0)
throw new ArgumentException
(String.Format
(@"Type '{0}' does not have a custom GuidAttribute set. Refusing to serialize.",
value.GetType().Name),
"value");
}
public override object ReadJson
(JsonReader reader,
Type objectType,
object existingValue,
JsonSerializer serializer)
{
JObject json = JObject.Load(reader);
var props = json.Properties().ToList();
var instanceId = (Guid) props[0].Value;
var typeId = (Guid) props[1].Value;
var name = (string) props[2].Value;
var data = props[3].Value;
var document = _Kernel.Get<IWeinCadDocument>(typeId.ToString());
document.PersistentData = data;
document.InstanceId = instanceId;
document.Name = name;
return document;
}
public override bool CanConvert(Type objectType)
{
return typeof (IWeinCadDocument).IsAssignableFrom(objectType);
}
public WeinCadDocumentConverter(IKernel kernel)
{
_Kernel = kernel;
}
}