1. ホーム
  2. c#

[解決済み] [Solved] JSON.NETでカスタムJsonConverterを実装する方法は?

2022-03-26 14:45:21

質問

私はここに示されているJSON.netの例を拡張しようとしています。 http://james.newtonking.com/projects/json/help/CustomCreationConverter.html

ベースクラス/インターフェースから派生した別のサブクラスがあります。

public class Person
{
    public string FirstName { get; set; }
    public string LastName { get; set; }
}

public class Employee : Person
{
    public string Department { get; set; }
    public string JobTitle { get; set; }
}

public class Artist : Person
{
    public string Skill { get; set; }
}

List<Person> people  = new List<Person>
{
    new Employee(),
    new Employee(),
    new Artist(),
};

以下のJsonをList< Personにデシリアライズするにはどうすればよいですか?

[
  {
    "Department": "Department1",
    "JobTitle": "JobTitle1",
    "FirstName": "FirstName1",
    "LastName": "LastName1"
  },
  {
    "Department": "Department2",
    "JobTitle": "JobTitle2",
    "FirstName": "FirstName2",
    "LastName": "LastName2"
  },
  {
    "Skill": "Painter",
    "FirstName": "FirstName3",
    "LastName": "LastName3"
  }
]

TypeNameHandlingのJsonSerializerSettingsは使いたくない。私は特に、これを処理するためのカスタム JsonConverter の実装を探している。このあたりのドキュメントや例は、ネット上ではかなりまばらである。私はJsonConverterでオーバーライドされたReadJson()メソッドの実装を正しく取得することができないようです。

どうすればいい?

標準の CustomCreationConverter を生成する方法に苦労しました。 Person または Employee というのも、これを決定するためにはJSONを分析する必要があり、そのための方法として Create メソッドを使用します。

型変換に関連するディスカッションスレッドを見つけ、それが答えとなることが判明しました。以下はそのリンクです。 タイプコンバート(アーカイブスリンク) .

必要なのは、サブクラス JsonConverter をオーバーライドし ReadJson メソッドを作成し、新しい抽象的な Create メソッドで JObject .

JObjectクラスは、JSONオブジェクトをロードするための手段を提供し は、このオブジェクト内のデータへのアクセスを提供します。

オーバーライドされた ReadJson メソッドは JObject を起動し Create メソッド (派生したコンバータクラスが実装) を呼び出し、そこに JObject のインスタンスを作成します。

これは JObject インスタンスを分析し、特定のフィールドの存在を確認することで、正しい型を決定することができます。

string json = "[{
        \"Department\": \"Department1\",
        \"JobTitle\": \"JobTitle1\",
        \"FirstName\": \"FirstName1\",
        \"LastName\": \"LastName1\"
    },{
        \"Department\": \"Department2\",
        \"JobTitle\": \"JobTitle2\",
        \"FirstName\": \"FirstName2\",
        \"LastName\": \"LastName2\"
    },
        {\"Skill\": \"Painter\",
        \"FirstName\": \"FirstName3\",
        \"LastName\": \"LastName3\"
    }]";

List<Person> persons = 
    JsonConvert.DeserializeObject<List<Person>>(json, new PersonConverter());

...

public class PersonConverter : JsonCreationConverter<Person>
{
    protected override Person Create(Type objectType, JObject jObject)
    {
        if (FieldExists("Skill", jObject))
        {
            return new Artist();
        }
        else if (FieldExists("Department", jObject))
        {
            return new Employee();
        }
        else
        {
            return new Person();
        }
    }

    private bool FieldExists(string fieldName, JObject jObject)
    {
        return jObject[fieldName] != null;
    }
}

public abstract class JsonCreationConverter<T> : JsonConverter
{
    /// <summary>
    /// Create an instance of objectType, based properties in the JSON object
    /// </summary>
    /// <param name="objectType">type of object expected</param>
    /// <param name="jObject">
    /// contents of JSON object that will be deserialized
    /// </param>
    /// <returns></returns>
    protected abstract T Create(Type objectType, JObject jObject);

    public override bool CanConvert(Type objectType)
    {
        return typeof(T).IsAssignableFrom(objectType);
    }

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

    public override object ReadJson(JsonReader reader, 
                                    Type objectType, 
                                     object existingValue, 
                                     JsonSerializer serializer)
    {
        // Load JObject from stream
        JObject jObject = JObject.Load(reader);

        // Create target object based on JObject
        T target = Create(objectType, jObject);

        // Populate the object properties
        serializer.Populate(jObject.CreateReader(), target);

        return target;
    }
}