Для чего используется параметр existingValue в методе ReadJson JsonConverter?

#json.net

#json.net

Вопрос:

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

 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 как обычное значение по умолчанию равно нулю.

 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();
    }
}
 

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

Ответ №3:

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

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