Search code examples
c#jsonjson.netdefault-valuejson-deserialization

Newtonsoft json DeserializeObject with DefaultValueHandling not working with runtime object


I'm trying to have a property of an object to return a default value in the absence of that property in the json I deserialize.

I read that I can achieve this using the [JsonProperty(DefaultValueHandling = DefaultValueHandling.Populate)] attribute.

    public class MyClass {
        public readonly string Id;
        [JsonProperty(DefaultValueHandling = DefaultValueHandling.Populate)]
        public RectangleF Position { get; set; } = new RectangleF(0, 0, 1, 1);

        [JsonConstructor]
        public MyClass(string id, RectangleF position) {
            Id = id;
            Position = position;
        }
    }

    [Test]
    public void DeserializeDefaultValue() {
        var json = JObject.FromObject(new { id = "123" });
        var obj = json.ToObject<MyClass>();
        Expect(obj.Position, Is.EqualTo(new RectangleF(0, 0, 1, 1)));
    }

The test fails, with the position always returning a new RectangleF(0, 0, 0, 0)

I can't have a [DefaultValue] attribute, like I see in a lot of examples, since RectangleF is initialized at runtime.

I tried many things such as having the [JsonProperty(DefaultValueHandling = DefaultValueHandling.Populate)] in the constructor, overloading the constructor with a default value for position, but nothing works.

Is there something simple I'm missing to achieve this?


Solution

  • So I modified what you were doing slightly but this works for what you are trying to accomplish.

    public class MyClass
    {
        public readonly string Id;
        public RectangleF Position { get; set; }
    
        [JsonConstructor]
        public MyClass(string id, RectangleF? position)
        {
            Id = id;
            Position = position ?? new RectangleF(0, 0, 1, 1);
        }
    }
    

    Just to add to this, if the only contractor you have assigns any value to a property, the property initializer is ignored (always). If you add a default constructor (with no params) then I believe what you have above will always work.