web-dev-qa-db-ja.com

JSON.NET:親(ホルダー)オブジェクトの値に基づいてインターフェイスプロパティをデシリアライズする方法

私はそのようなクラスを持っています

class Holder {
    public int ObjType { get; set; }
    public List<Base> Objects { get; set; }
}

abstract class Base {
    // ... doesn't matter
}

class DerivedType1 : Base {
    // ... doesn't matter
}

class DerivedType2 : Base {
    // ... doesn't matter
}

WebAPIを使用して、オブジェクトHolderを受け取り、正しくデシリアライズします。 ObjTypeの値に基づいて、ObjectsプロパティをList<DerivedType1>ObjType == 1)またはList<DerivedType2>ObjType == 2)としてデシリアライズする必要があります。

現時点で私はSOとインターネットで最良のアプローチを検索しましたが、私が見つけた最良のものはこの答えです https://stackoverflow.com/a/8031283/1038496 =。このソリューションの問題は、親オブジェクトのコンテキストが失われるため、ObjTypeの値を見つけることができないことです。OK、JsonConverterのカスタムHolderを作成し、ObjTypeの値を覚えることで解決できますが、それでも私はこの線の不安

serializer.Populate(jObject.CreateReader(), target);

この回答の下のコメントが言うように

ReadJsonメソッドで作成された新しいJsonReaderは、元のリーダーの構成値(Culture、DateParseHandling、DateTimeZoneHandling、FloatParseHandlingなど)を継承しません。これらの値は、serializer.Populate()で新しいJsonReaderを使用する前にコピーする必要があります。

これは私にとって問題であり、これらの値を自分でコピーすることは私にはきれいに見えません(何かを逃した場合はどうなりますか?)

したがって、質問は次のとおりです:親プロパティ値に基づいて抽象オブジェクトプロパティを逆シリアル化するために逃したより良いアプローチはありますか?

24
Zoka

あなたは正しい軌道に乗っています。あなたが提案したように、この状況を処理するには、JsonConverterクラスにカスタムHolderを実装する必要があります。ただし、心配する必要はありません。コンバーターに渡された元のリーダーとシリアライザーのインスタンスを使用して、新しいインスタンスに設定をコピーする必要がないようにコンバーターを作成することが可能です。これが私がそれを書く方法です:

class HolderConverter : JsonConverter
{
    public override bool CanConvert(Type objectType)
    {
        return (objectType == typeof(Holder));
    }

    public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
    {
        JObject jo = JObject.Load(reader);
        Holder holder = new Holder();
        holder.ObjType = (int)jo["ObjType"];
        holder.Objects = new List<Base>();
        foreach (JObject obj in jo["Objects"])
        {
            if (holder.ObjType == 1)
                holder.Objects.Add(obj.ToObject<DerivedType1>(serializer));
            else
                holder.Objects.Add(obj.ToObject<DerivedType2>(serializer));
        }
        return holder;
    }

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

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

ここに簡単なデモがあります:

class Program
{
    static void Main(string[] args)
    {
        string json = @"
        [
            {
                ""ObjType"" : 1,
                ""Objects"" : 
                [
                    { ""Id"" : 1, ""Foo"" : ""One"" },
                    { ""Id"" : 2, ""Foo"" : ""Two"" },
                ]
            },
            {
                ""ObjType"" : 2,
                ""Objects"" : 
                [
                    { ""Id"" : 3, ""Bar"" : ""Three"" },
                    { ""Id"" : 4, ""Bar"" : ""Four"" },
                ]
            },
        ]";

        List<Holder> list = JsonConvert.DeserializeObject<List<Holder>>(json);

        foreach (Holder holder in list)
        {
            if (holder.ObjType == 1)
            {
                foreach (DerivedType1 obj in holder.Objects)
                {
                    Console.WriteLine("Id: " + obj.Id + "  Foo: " + obj.Foo);
                }
            }
            else
            {
                foreach (DerivedType2 obj in holder.Objects)
                {
                    Console.WriteLine("Id: " + obj.Id + "  Bar: " + obj.Bar);
                }
            }
        }
    }
}

[JsonConverter(typeof(HolderConverter))]
class Holder
{
    public int ObjType { get; set; }
    public List<Base> Objects { get; set; }
}

abstract class Base
{
    public int Id { get; set; }
}

class DerivedType1 : Base
{
    public string Foo { get; set; }
}

class DerivedType2 : Base
{
    public string Bar { get; set; }
}

出力:

Id: 1  Foo: One
Id: 2  Foo: Two
Id: 3  Bar: Three
Id: 4  Bar: Four
47
Brian Rogers