Default value for missing properties with JSON.net

C#json.net

C# Problem Overview


I'm using Json.net to serialize objects to database.

I added a new property to the class (which is missing in the json in the database) and I want the new property to have a default value when missing in the json.

I tried DefaultValue attribute but it's doesn't work. I'm using private setters and constructor to deserialize the json, so setting the value of the property in the constructor will not work as there is a parameter with the value.

Following is an example:

    class Cat
    {
        public Cat(string name, int age)
        {
            Name = name;
            Age = age;
        }

        public string Name { get; private set; }
        
        [DefaultValue(5)]            
        public int Age { get; private set; }
    }

    static void Main(string[] args)
    {
        string json = "{\"name\":\"mmmm\"}";

        Cat cat = JsonConvert.DeserializeObject<Cat>(json);

        Console.WriteLine("{0} {1}", cat.Name, cat.Age);
    }

I expect the age to be 5 but it is zero.

Any suggestions?

C# Solutions


Solution 1 - C#

I found the answer, just need to add the following attribute as well:

[JsonProperty(DefaultValueHandling = DefaultValueHandling.Populate)]

In your example:

class Cat
{
    public Cat(string name, int age)
    {
        Name = name;
        Age = age;
    }

    public string Name { get; private set; }

    [DefaultValue(5)]            
    [JsonProperty(DefaultValueHandling = DefaultValueHandling.Populate)]
    public int Age { get; private set; }
}

static void Main(string[] args)
{
    string json = "{\"name\":\"mmmm\"}";

    Cat cat = JsonConvert.DeserializeObject<Cat>(json);

    Console.WriteLine("{0} {1}", cat.Name, cat.Age);
}

See Json.Net Reference

Solution 2 - C#

You can also have a default value as:

class Cat
{           
    public string Name { get; set; }
        
    public int Age { get; set; } = 1 ; // one is the default value. If json property does not exist when deserializing the value will be one. 
}

Solution 3 - C#

Add [JsonProperty(DefaultValueHandling = DefaultValueHandling.Populate)] ,Change your Age property from

    [DefaultValue(5)]            
    public int Age { get; private set; }

to

    [JsonProperty(DefaultValueHandling = DefaultValueHandling.Populate)]
    [DefaultValue(5)]
    public string Age { get; private set; }

Solution 4 - C#

I had same issue as someone already described where probably existing constructor made DefaultValueHandling not working. I played a bit around and found out that it can be bypassed with NullValueHandling.

[DefaultValue(5)]
[JsonProperty(DefaultValueHandling = DefaultValueHandling.Populate, NullValueHandling = NullValueHandling.Ignore)]
public int Age { get; set; }

Solution 5 - C#

As the question's title is phrased more broadly (than just handling a single default value for one single missing JSON property), here's a more generic solution for handling missing JSON properties on a global level:

To handle all missing JSON properties in more complex models, a ContractResolver is likely needed. (other means are inconsistent with missing properties or null value handling in general)

The idea is to use DefaultContractResolver.CreateProperty() to "hook" into the creation of JSON properties and specify desired behavior, which will run on all members (also nested ones) relevant during the (de-)serialization process:

public class MyContractResolver : DefaultContractResolver
{
    protected override JsonProperty CreateProperty(MemberInfo member, MemberSerialization memberSerialization)
    {
        JsonProperty prop = base.CreateProperty(member, memberSerialization);

        // do smth with the property (settings) here

        return prop;
    }
}

Here is an example for how this can be used to default-initialize all collection types to empty collections instead of null, when they are missing (what initially brought me to this question):

public class EmptyCollectionJsonResolver : DefaultContractResolver
{
    protected override JsonProperty CreateProperty(MemberInfo member, MemberSerialization memberSerialization)
    {
        JsonProperty prop = base.CreateProperty(member, memberSerialization);

        // default initialize collections to empty instead of null
        if (IsCollection(prop.PropertyType))
        {
            prop.DefaultValue = Activator.CreateInstance(prop.PropertyType); // creates empty collection
            prop.DefaultValueHandling = DefaultValueHandling.Populate; // force population on null / missing parsed members
        }

        return prop;
    }

    // helper function to determine if a type is a collection
    private static readonly Type TColl = typeof(ICollection<>);
    private static bool IsCollection(Type t)
    {
        return t.IsGenericType 
            && TColl.IsAssignableFrom(t.GetGenericTypeDefinition()) || t.GetInterfaces().Any(x => x.IsGenericType && x.GetGenericTypeDefinition() == TColl);
    }
}

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionsomdoronView Question on Stackoverflow
Solution 1 - C#somdoronView Answer on Stackoverflow
Solution 2 - C#Tono NamView Answer on Stackoverflow
Solution 3 - C#Karthikeyan VKView Answer on Stackoverflow
Solution 4 - C#David MikulaView Answer on Stackoverflow
Solution 5 - C#VinzView Answer on Stackoverflow