2014-06-12 42 views
1

我使用JSON.Net來保存從接口繼承的對象的集合。我發現我可以設置TypeNameHandling如何在重構安全的方式使用JSON.Net持久數據

JsonSerializerSettings.TypeNameHandling = TypeNameHandling.Auto; 

,我可以得到注入發出JSON對象可在序列被用於構建正確的對象的完全限定類型名稱。

"$type": "Newtonsoft.Json.Samples.Hotel, Newtonsoft.Json.Tests" 

但是我非常喜歡我的resharper重構,並且這種映射會在代碼版本之間快速中斷。

我想要的不是存儲完全限定的類型名稱我將每個類都分配一個Guid作爲類型id,它在重構下永遠不會改變,並使用該類型映射回反序列化代碼。

這是某人曾經嘗試過的東西,如果是的話如何去做這件事?

回答

2

編輯

我們現在有一個開源項目,以幫助遷移。

https://github.com/Weingartner/Migrations.Json.Net

原來的答案

這裏是JsonConverter我寫的解決問題。我所做的是使用Guid屬性(通常用於com互操作)來標記我的序列化。然後我提供了一個基於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; 
    } 
}