Каков параметр existingValue, используемый в методе ReadJson для JsonConverter?

При создании настраиваемого Json Converter одним из методов, который необходимо переопределить, является:

public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)

Что такое параметр "existingValue"? Что означает имя переменной "existingValue" в этом контексте?

Ответы

Ответ 1

Проще говоря, параметр existingValue дает вам существующее или значение по умолчанию для объекта, которое в конечном итоге будет заменено значением, возвращаемым методом ReadJson. Это дает методу ReadJson возможность оценить существующее значение при определении того, что нужно вернуть. Метод мог бы, например, принять решение о сохранении значения по умолчанию или каким-либо образом объединить его с десериализованным значением от читателя.

Рассмотрим следующий пример. Этот конвертер десериализует целочисленное значение из JSON и возвращает сумму этого значения и существующее значение для десериализации поля.

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

    public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
    {
        JToken token = JToken.Load(reader);
        return (int)existingValue + token.ToObject<int>();
    }

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

Теперь скажем, что у нас есть класс Foo, который имеет два свойства int, Value1 и Value2, оба из которых используют этот конвертер. Value1 присваивается значение по умолчанию 42 в конструкторе, а Value2 имеет стандартное значение по умолчанию 0.

class Foo
{
    [JsonConverter(typeof(AdditiveIntConverter))]
    public int Value1 { get; set; }

    [JsonConverter(typeof(AdditiveIntConverter))]
    public int Value2 { get; set; }

    public Foo()
    {
        Value1 = 42;
    }
}

Если мы десериализуем некоторые данные в этот класс...

class Program
{
    static void Main(string[] args)
    {
        string json = @"{ ""Value1"" : 18, ""Value2"" : 33 }";

        Foo foo = JsonConvert.DeserializeObject<Foo>(json);
        Console.WriteLine("Value1: " + foo.Value1);
        Console.WriteLine("Value2: " + foo.Value2);
    }
}    

... получаем следующий результат:

Value1: 60
Value2: 33

Конечно, это всего лишь надуманный пример. На практике нет необходимости использовать параметр existingValue при реализации JsonConverter, и большую часть времени его значение будет либо нулевым, либо нулевым. Вы можете смело игнорировать его.

Ответ 2

Я считаю, что основной пример использования - это "заполнение" существующих свойств, значения которых изменяемы, но которые сами по себе не доступны для записи. Например:

public class A {
    private readonly List<int> b = new List<int> { 1 };
    public List<int> B { get { return this.b; } }
}

JsonConvert.DeserializeObject<A>("{ B: [2] }").Dump(); // B has both 1 and 2!

Теперь скажем, что вместо списка с именем B мы имеем свойство только для чтения настраиваемого типа:

// try this code in LinqPad!
void Main()
{
    JsonConvert.DeserializeObject<A>("{ C: { Y: 5 } }").Dump();
}

// Define other methods and classes here
public class A {
    private readonly C c = new C { X = 1 };
    public C C { get { return this.c; } }
}

[JsonConverter(typeof(CJsonConverter))]
public class C {
    public int X { get; set; }
    public int Y { get; set; }
}

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

    public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
    {
        // VERSION 1: don't use existingValue
        //var value = new C();
        // VERSION 2: use existingValue
        var value = (C)existingValue ?? new C();

        // populate value
        var dict = serializer.Deserialize<Dictionary<string, int>>(reader);
        if (dict.ContainsKey("X")) { value.X = dict["X"]; }
        if (dict.ContainsKey("Y")) { value.Y = dict["Y"]; }
        return value;
    }

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

Обратите внимание, что код показывает два способа записи конвертера. Один использует существующее значение, другое - нет. Если мы используем существующее значение, мы десериализуем "{ C: { Y: 5 } }" в: { C: { X: 1, Y:5 } }, сохраняя при этом значение по умолчанию X = 1 и заполняя свойство только для чтения C. С другой стороны, если мы не используем существующее значение и всегда имеем конвертер создает новый C, тогда мы не заполняем свойство C только для чтения.

Ответ 3

existingValue - необязательное значение (проверка для null), которое может быть частично десериализовано другим способом или базовым методом. Обычно это значение равно null, но при установке свойства, имеющего JsonConverter, может быть ненулевым.

Я не видел никаких реализаций ReadJson, которые используют это значение (но это не значит, что их нет).