2016-02-18 180 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