Default value for missing properties with JSON.net
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
JSON.net, C#, unable to set default values in data model
You are using the wrong setting.
If you want null
values in the JSON to be ignored during deserialization, you need to set NullValueHandling = NullValueHandling.Ignore
in the settings. The default is Include
, which means that if the JSON has an explicit null
in it for a particular property, that property will be set to null on your object, overwriting any default you may have set.
DefaultValueHandling.Populate
means if the key is missing altogether in the JSON (which is different from being set to null), then it will use the value provided by the [DefaultValue]
attribute. Note that if the JSON has an explicit null
in it, this setting will not use the default value from the attribute, even if NullValueHandling
is set to Ignore
. So if you want a default value in that situation, the best way to do that is by setting it in the constructor.
So, in short, to get the behavior you want:
- Use the
NullValueHandling.Ignore
setting. - Provide your default values via the constructor.
- You don't need the
DefaultValueHandling
setting or the[DefaultValue]
attribute.
Demo fiddle: https://dotnetfiddle.net/kyUjFz
Specifying default value for a property in relation to other property in Json.NET
As specified in the JSON standard, a JSON object is an unordered set of name/value pairs, so in general Json.NET does not allow one property to be set relative to another. Json.NET is a streaming, single-pass deserializer and there's no guarantee which will appear first in the JSON.
However, when your object specifies use of a parameterized constructor via, e.g., the [JsonConstructor]
attribute, Json.NET will pre-load the JSON properties and their values then construct the object with the deserialized values. This affords an opportunity to set the End
property relative to the Start
property:
public partial class JsonsoftExample
{
public JsonsoftExample() { }
[JsonConstructor]
JsonsoftExample(DateTime start, DateTime? end)
{
this.Start = start;
this.End = end ?? Start.AddHours(1);
}
[JsonProperty(Required = Required.Always)]
public DateTime Start { get; set; }
public DateTime End { get; set; }
}
Notes:
The names of the constructor arguments must be the same as the names of the properties, modulo case. This is how Json.NET matches constructor arguments with JSON properties.
Note that the
End
property is of typeDateTime
while theend
constructor argument is of typeDateTime?
. When"End"
is missing from the JSON, Json.NET will pass in anull
value into the constructor, which can be checked for to properly initialize theEnd
time relative to theStart
.If you don't want to serialize the
End
property when it is exactly one hour later than the start, you can use conditional property serialization:public bool ShouldSerializeEnd()
{
return End != Start.AddHours(1);
}The constructor does not need to be public when marked with
[JsonConstructor]
. Properties not passed into the constructor will be populated after construction as per usual deserialization.
Sample fiddle.
Json.net doesn't populate properties with default values when defined in the constructor
I was able to reproduce the change from Json 6.0.8 to 9.0.1. It appears that JsonSerializerInternalReader.CreateObjectUsingCreatorWithParameters()
now checks for the presence of the [DefaultValue(DefaultText)]
and [JsonProperty(DefaultValueHandling = DefaultValueHandling.Populate)]
attributes on the constructor parameter rather than the corresponding property. This check is made around line 1979:
if (HasFlag(constructorProperty.DefaultValueHandling.GetValueOrDefault(Serializer._defaultValueHandling), DefaultValueHandling.Populate))
{
context.Value = EnsureType(
reader,
constructorProperty.GetResolvedDefaultValue(),
CultureInfo.InvariantCulture,
constructorProperty.PropertyContract,
constructorProperty.PropertyType);
}
Thus you can deserialize your type successfully by modifying the constructor as follows:
class MyTestClass
{
public const string DefaultText = "...";
[DefaultValue(DefaultText)]
[JsonProperty(PropertyName = "myText", DefaultValueHandling = DefaultValueHandling.Populate)]
public readonly string Text;
public MyTestClass([JsonProperty(DefaultValueHandling = DefaultValueHandling.Populate), DefaultValue(DefaultText)] string text = DefaultText)
{
Text = text;
}
}
I'm not sure if this is an intentional or unintentional change. You might want to report an issue to ask. Or perhaps you already have?
Update
As another workaround, you could give your type a private parameterless constructor that sets the default value correctly, and mark it with [JsonConstructor]
:
class MyTestClass
{
public const string DefaultText = "...";
[DefaultValue(DefaultText)]
[JsonProperty(PropertyName = "myText", DefaultValueHandling = DefaultValueHandling.Populate)]
public readonly string Text;
[JsonConstructor]
MyTestClass()
: this(DefaultText)
{
}
public MyTestClass(string text = DefaultText)
{
Text = text;
}
}
Json.NET will now call the private parameterless constructor, then correctly set the value for the readonly field Text
through reflection, since it has been marked with [JsonProperty]
. (Or even make the parameterless constructor public and make the parameter to the second constructor non-optional.)
Why when I deserialize with JSON.NET ignores my default value?
The DefaultValue
attribute does not set the value of the property. See this question: .NET DefaultValue attribute
What you might be better off doing is setting the value in the constructor:
public class AssignmentContentItem
{
[JsonProperty("Id")]
public string Id { get; set; }
[JsonProperty("Qty")]
public int Quantity { get; set; }
public AssignmentContentItem()
{
this.Quantity = 1;
}
}
Where this line:
AssignmentContentItem item =
JsonConvert.DeserializeObject<AssignmentContentItem>("{\"Id\":\"Q0\"}");
Results in an AssignmentContentItem
with its Quantity
set to 1
.
Ignoring some default values for serialization
You can use a variation of the DefaultValueContractResolver
from this answer to Json.NET: How to make DefaultValueHandling only apply to certain types? to exclude all enum-valued properties with default values:
public class EnumDefaultValueContractResolver : DefaultContractResolver
{
protected override JsonProperty CreateProperty(MemberInfo member, MemberSerialization memberSerialization)
{
var property = base.CreateProperty(member, memberSerialization);
if (property.DefaultValueHandling == null)
{
if (property.PropertyType.IsEnum)
{
//For safety you could check here if the default value is named ValueNotSet and only set IgnoreAndPopulate in that case.
//var defaultValue = Enum.ToObject(property.PropertyType, 0);
//if (defaultValue.ToString() == "ValueNotSet")
//{
property.DefaultValueHandling = DefaultValueHandling.IgnoreAndPopulate; // Or DefaultValueHandling.Ignore if you prefer
//}
}
}
return property;
}
}
Then use it as follows:
var resolver = new EnumDefaultValueContractResolver();
var settings = new JsonSerializerSettings { ContractResolver = resolver };
var json = JsonConvert.SerializeObject(inventory, settings);
You may want to cache the contract resolver for best performance.
Demo fiddle here.
Related Topics
Wpf Webbrowser (3.5 Sp1) Always on Top - Other Suggestion to Display HTML in Wpf
System.Data.Sqlclient.Sqlexception: Invalid Column Name 'Phone_Types_Phone_Type_Id'
How to Pass Current User Information to All Layers in Ddd
Print HTML Document from Windows Service in C# Without Print Dialog
Fixing Gridview Header While Scrolling
How to Password Encrypt SQLite Database
JavaScript Serialization of Datetime in ASP.NET Is Not Giving a JavaScript Date Object
How to Add Attributes for C# Xml Serialization
Windows Phone 8: Media File Access
Preventing SQL Injection on ASP.NET Web Application
How to Open the Physical File Operating System Error 32
How Could the New Async Feature in C# 5.0 Be Implemented with Call/Cc
With C# Use Chrome to Covert HTML to Pdf
How to Conditionally Control The Visibility of a Control in Asp.Net
Which Linux Distribution Is Best for Developing a Mono Application in a Virtual Machine