What is the existingValue parameter used for in the ReadJson method of a JsonConverter?

北慕城南 提交于 2019-12-10 00:36:37

问题


When creating a custom Json Converter one of the methods which needs to be overridden is:

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

What is "existingValue" parameter used for? What does the variable name "existingValue" mean in this context?


回答1:


Simply put, the existingValue parameter gives you the existing or default value of the object that will ultimately be replaced with the value returned from the ReadJson method. This gives the ReadJson method the chance to evaluate the existing value when determining what to return. The method could, for example, decide to keep the default, or combine it in some way with the deserialized value from the reader if desired.

Consider the following example. This converter will deserialize an integer value from the JSON and return the sum of that value and the existing value for the field being deserialized to.

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

Now let's say we have a class Foo which has two int properties, Value1 and Value2, both of which use this converter. Value1 is assigned a default value of 42 in the constructor, while Value2 has the usual default value of zero.

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

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

    public Foo()
    {
        Value1 = 42;
    }
}

If we deserialize some data into this class...

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

...we get the following result:

Value1: 60
Value2: 33

Of course, this is just a contrived example. In practice, there is not much of a need to use the existingValue parameter when implementing a JsonConverter, and most of the time its value will be either null or zero. You can safely ignore it.




回答2:


I believe the primary use-case for this is for "populating" existing properties whose values are mutable but which are not themselves writable. For example:

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!

Now, let's say that instead of a list called B we have a read-only property of a custom type:

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

Note that the code shows 2 ways to write the converter. One uses existing value, the other doesn't. If we use existing value, we deserialize "{ C: { Y: 5 } }" into: { C: { X: 1, Y:5 } }, thus preserving the default of X = 1 and populating the read-only property C. On the other hand, if we do not use existing value and always have our converter create a new C, then we fail to populate the read-only C property at all.




回答3:


existingValue is an optional value (check for null) that may be partially deserialized by another method or by a base method. This is normally null, but may be non-null when setting a property who has a JsonConverter.

I haven't seen any ReadJson implementations that use this value (but that doesn't mean there aren't any).



来源:https://stackoverflow.com/questions/24347613/what-is-the-existingvalue-parameter-used-for-in-the-readjson-method-of-a-jsoncon

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!