我正在使用 JSON.Net 保存从接口(interface)继承的对象集合。我发现我可以设置 TypeNameHandling如
JsonSerializerSettings.TypeNameHandling = TypeNameHandling.Auto;
我可以获得注入(inject)到发出的 json 中的对象的完全限定类型名称,该名称可用于序列化以构造正确的对象。
"$type": "Newtonsoft.Json.Samples.Hotel, Newtonsoft.Json.Tests"
但是,我对 resharper 重构非常满意,而且这种映射会在代码版本之间快速中断。
我想要的是,我不想存储完全限定的类型名,而是为每个类分配一个 Guid 作为类型 id,它在重构下永远不会改变,并使用它映射回反序列化代码。
这是有人以前尝试过的吗?如果是的话,该如何去做?
最佳答案
编辑
我们现在有一个开源项目来帮助迁移。
https://github.com/Weingartner/Migrations.Json.Net
原始答案
这是我为解决我的问题而编写的 JsonConverter。我所做的是使用 Guid 属性(通常用于 com 互操作)来标记我的序列化。然后我根据指南提供了一个工厂。该工厂基于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;
}
}
关于c# - 如何以重构安全的方式使用 JSON.Net 来获取持久数据,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/24179663/