インターフェイスのコレクションを逆シリアル化すると、カスタムコンバーターを使用した以前のデータモデルからのJSONの逆シリアル化が失敗します

gvoysey

私は、ユーザーがローカルキャッシュにJSONシリアル化されたPOCOを永続化して、たとえばの状態を保持するゲームシステムを維持する責任があります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
}

私はさらにカスタムコンバータークラスを定義しました(これこれを読んだこと

しかし、コレクションの逆シリアル化でまだ問題が発生しています。

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>私が見ることができるどのコードパスにもそれをキャストすることはできませんこれをどのように解決すればよいですか?

dbc

レガシー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; }
}

この記事はインターネットから収集されたものであり、転載の際にはソースを示してください。

侵害の場合は、連絡してください[email protected]

編集
0

コメントを追加

0

関連記事

Related 関連記事

ホットタグ

アーカイブ