Search code examples
c#json.netjsonconverter

Access already deserialized properties from JsonConverter.ReadJson


I am trying to solve backward compartibility when deserializing old json. Previously there was double property and now it's changed to a custom type.

My idea is to read double and simply convert it using custom json converter.

Before was:

public class A
{
    [JsonProperty)]
    string Name { get; set; }
    [JsonProperty)]
    double Value { get; set; }
}

Serialized as

{"Name":"test","Value":33.0}

New one:

public class A
{
    [JsonProperty]
    [JsonConverter(typeof(MyJsonConverter))]
    public MyType Value { get; set; }
}

Serialized as

{"Value":{"Value":33.0,"Name":"test", ...}},

Converter:

public class MyJsonConverter : JsonConverter
{
    public override bool CanConvert(Type objectType) => true;

    public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
    {
        if (reader.Value is double value)
            return new MyType(value, ???); // here is the problem, I need Name value here
        return reader.Value;
    }

    public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer) =>
        JToken.FromObject(value).WriteTo(writer);
}

but to construct MyType I need string parameter which is a value of another property Name

How to access Name value from converter for Value? How to access anything what has been deserialized? Is there some kind of tree? Tokens tree?

Another thing: in WriteJson method I want to do "nothing" special, is my implementation correct? Or is there an easy way to prevent converter doing anything "special" upon serialization?


Solution

  • You would need to apply a converter to your parent A class:

    [JsonConverter(typeof(MyJsonConverter))]
    public class A
    {
        public MyType Value { get; set; }
    }
    
    public class AConverter : JsonConverter
    {
        public override bool CanConvert(Type objectType) => objectType == typeof(A);
    
        public override bool CanWrite => false;
    
        public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
        {
            JObject jObject = JObject.Load(reader);
    
            // Check if the keys contains "Name"
            string name = jObject["Name"]?.ToString();
    
            var a = new A();
    
            if (name != null)
            {
                a.Value = new MyType
                {
                    Name = name,
                    Value = jObject["Value"].Value<double>()
                };
            }
            else
            {
                a.Value = jObject["Value"].ToObject<MyType>();
            }
    
            return a;
        }
    
        public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
        {
            throw new NotImplementedException();
        }
    }
    

    To use default serailisation, just override CanWrite with false.