Json.Net - десериализовать JSON в словарь со специальной обработкой для определенного поля - PullRequest
1 голос
/ 28 марта 2019

https://dotnetfiddle.net/ka6XVw - Fiddle с примером структуры типа

Предположим, у меня есть класс, который реализует IDictionary<string, T>.Json.Net может десериализовать такие типы из коробки, создав экземпляр типа и используя его индексатор для заполнения словаря.Проблема заключается в том, что этот класс также наследует строковое свойство Error, помеченное атрибутом JsonProperty, из своего базового класса, и я хотел бы, чтобы это свойство заполнялось всякий раз, когда входной json содержит поле error.Однако при десериализации IDictionary Json.Net считает, что все поля являются словарными статьями, и пытается добавить значение со словарем error в словарь.

Какой самый простой и чистый способ десериализацииjson в словарь и поле error в свойство Error?Обратите внимание, что класс является общим, поэтому JsonExtensionData не является опцией (без приведения его значений к указанному типу).

Пример допустимого словаря json: { 'foo': '1', 'bar': '2' }

Пример ошибки json{ 'error': 'blah' }

1 Ответ

1 голос
/ 28 марта 2019

Я получил решение от этого вопроса . По сути, вы присоединяете конвертер к своему классу DictionaryResponse и интерпретируете входящий JSON самостоятельно. Я был достаточно ленив, чтобы использовать JObject для анализа:

class DictionaryResponseConverter : JsonConverter<ResponseBase>
{
    public override ResponseBase ReadJson(
        JsonReader reader, Type objectType,
        ResponseBase existingValue, bool hasExistingValue,
        JsonSerializer serializer)
    {
        // find the correct T and call the internal function through reflection
        // as DictionaryResponse<T> is sealed, we don't care about inheritance
        return (ResponseBase)GetType()
            .GetMethod(nameof(InternalReadJson),
                       BindingFlags.Instance | BindingFlags.NonPublic)
            .MakeGenericMethod(objectType.GetGenericArguments()[0])
            .Invoke(this, new object[]
            {
                reader,
                existingValue,
                hasExistingValue,
                serializer
            });
    }

    DictionaryResponse<T> InternalReadJson<T>(
        JsonReader reader,
        DictionaryResponse<T> existingValue, bool hasExistingValue,
        JsonSerializer serializer)
    {
        if (reader.TokenType == JsonToken.Null)
            return null;
        var obj = JObject.Load(reader);
        var error = (string)obj["error"];
        bool hadError = obj.Remove("error");
        //var result = new DictionaryResponse<T>();
        var result = hasExistingValue ? existingValue : new DictionaryResponse<T>();
        foreach (var kvp in obj)
            result[kvp.Key] = kvp.Value.ToObject<T>();
        if (hadError)
            result.Error = error;
        return result;
    }

    public override void WriteJson(
        JsonWriter writer, ResponseBase value, JsonSerializer serializer)
    {
        // don't care about serialization
        throw new NotImplementedException();
    }
}
[JsonConverter(typeof(DictionaryResponseConverter))]
internal sealed class DictionaryResponse<T> : ResponseBase, IDictionary<string, T>
{
    ...
...