2016-02-18 179 views
3

的集合時,我負責維護一個遊戲系統,用戶堅持JSON序列波蘇斯在本地緩存中保存的,例如,一個Character狀態失敗。反序列化反序列化接口

代碼的最新版本已改變這些序列化對象的數據模型。特別是,創建了一個新的界面。這是在將舊的字符反序列化爲新代碼時產生的問題。我試圖用自定義轉換器來解決這些問題,但我遇到了麻煩。

老,系列化的版本:

public class Character{ 
    public Skill Parent {get;set;} 
    public Dictionary<string,Skill} Skills {get;set;} 
} 

public class Skill { 
//normal stuff here. 
} 

新版本:

public class Character{ 
    [JsonProperty, JsonConverter(typeof(ConcreteTypeConverter<Dictionary<string,Skill>>))] 
    public Dictionary<string,ISkill} Skills {get;set;} 
} 

public class Skill:ISkill { 
//normal stuff here. 
} 

public interface ISkill{ 
//stuff that all skill-like things have here 
} 

我進一步定義了自定義轉換器類(看了thisthis

但我仍然遇到麻煩反序列化收集。

public class Extensions 
{ 

//a lot of serializer extensions here including the Deserialize method 
private static readonly CustomSerializationBinder Binder = new CustomSerializationBinder(); 
     private static readonly JsonSerializerSettings JsonSerializerSettings = new JsonSerializerSettings 
     { 
      NullValueHandling = NullValueHandling.Ignore, 
      ReferenceLoopHandling = ReferenceLoopHandling.Ignore, 
      TypeNameHandling = TypeNameHandling.Objects, 
      TypeNameAssemblyFormat = System.Runtime.Serialization.Formatters.FormatterAssemblyStyle.Simple, 
      Binder = Binder, 
     }; 
} 


public class CustomSerializationBinder : DefaultSerializationBinder 
    { 


     public override Type BindToType(string assemblyName, string typeName) 
     { 

      return base.BindToType(assemblyName, typeName); 


     } 
    } 

    public class ConcreteTypeConverter<T> : JsonConverter 
    { 
     public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer) 
     { 
      serializer.Serialize(writer,value); // serialization isnt't the problem. 
     } 

     public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer) 
     { 
      if (typeof (T)==typeof(Dictionary<string,Skill>)) 
      { 
       var retVal = new object(); 
       if (reader.TokenType == JsonToken.StartObject) 
       { 
        T instance = (T)serializer.Deserialize(reader, typeof(T)); //crashes here 
        retVal = new List<T>() { instance }; 
        return retVal; 
       } 
      } 

      return serializer.Deserialize<T>(reader); 
     } 

     public override bool CanConvert(Type objectType) 
     { 
      return true; // kind of a hack 
     } 
    } 

所以我有一個Dictionary<string,Skill>,我不能投,要Dictionary<string,ISkill>中,我可以看到任何代碼路徑。我應該如何解決這個問題?

+0

看看https://stackoverflow.com/questions/33321698/jsonconverter-with-interface。這給出了一個通用轉換器,通過使用'$ type'(如果存在)或者如果不存在,選擇最佳屬性匹配,將確定哪個具體的'Skill'類爲'ISkill'接口反序列化。 – dbc

+0

我會看看。我已經使用'$ type'來解決一些問題(以膨脹爲代價),所以也許這是一個很好的前進方向。 我擔心的是,陳舊的JSON都將有一個'$ type'這是'Skill',不'ISkill'問題 - 還有,如何處理'$ type'這是'詞典<字符串,技能>'。 – gvoysey

+0

我不是100%確定我理解你的問題。您的遺留JSON(從「Skill」到「ISkill」重構之前)是否包含字典本身的「$ type」信息? – dbc

回答

0

由於舊版JSON已經包含鍵入包括字典對象,你需要做的就是的字典類型信息,並允許反序列化字典類型的代碼,而不是JSON控制的所有對象的信息。

下轉換器應該做的工作:

public class IgnoreDictionaryTypeConverter : JsonConverter 
{ 
    public override bool CanConvert(Type objectType) 
    { 
     return objectType.GetDictionaryKeyValueTypes().Count() == 1; 
    } 

    public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer) 
    { 
     if (reader.TokenType == JsonToken.Null) 
      return null; 
     existingValue = existingValue ?? serializer.ContractResolver.ResolveContract(objectType).DefaultCreator(); 
     var obj = JObject.Load(reader); 
     obj.Remove("$type"); 
     using (var subReader = obj.CreateReader()) 
     { 
      serializer.Populate(subReader, existingValue); 
     } 
     return existingValue; 
    } 

    public override bool CanWrite { get { return false; } } 

    public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer) 
    { 
     throw new NotImplementedException(); 
    } 
} 

public static class TypeExtensions 
{ 
    /// <summary> 
    /// Return all interfaces implemented by the incoming type as well as the type itself if it is an interface. 
    /// </summary> 
    /// <param name="type"></param> 
    /// <returns></returns> 
    public static IEnumerable<Type> GetInterfacesAndSelf(this Type type) 
    { 
     if (type == null) 
      throw new ArgumentNullException(); 
     if (type.IsInterface) 
      return new[] { type }.Concat(type.GetInterfaces()); 
     else 
      return type.GetInterfaces(); 
    } 

    public static IEnumerable<Type[]> GetDictionaryKeyValueTypes(this Type type) 
    { 
     foreach (Type intType in type.GetInterfacesAndSelf()) 
     { 
      if (intType.IsGenericType 
       && intType.GetGenericTypeDefinition() == typeof(IDictionary<,>)) 
      { 
       yield return intType.GetGenericArguments(); 
      } 
     } 
    } 
} 

然後,你可以在設置中添加它,或者它適用於有問題的字典屬性:

public class Character 
{ 
    [JsonConverter(typeof(IgnoreDictionaryTypeConverter))] 
    public IDictionary<string, ISkill> Skills { get; set; } 
} 

對於未來,你可能也希望禁用發射的用於字典類型的信息,因爲字典是集合和集合類型更好的代碼,而不是JSON規定:

public class Character 
{ 
    [JsonConverter(typeof(IgnoreDictionaryTypeConverter))] 
    [JsonProperty(TypeNameHandling = TypeNameHandling.None)] 
    public IDictionary<string, ISkill> Skills { get; set; } 
} 
+0

這與一個自定義SerializationBinder,做了伎倆。謝謝! – gvoysey