Каков параметр 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
, которые используют это значение (но это не значит, что их нет).