#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
одной реализации, которая использовала бы это значение (но это не значит, что их нет).