Invalid deserialisation order for required fields
See original GitHub issueSource JSON
{}
Destination type
public class SampleType
{
[JsonProperty(Required = Required.Always)]
public int SampleField { get; }
[JsonConstructor]
public SampleType(int sampleField)
{
if (sampleField == 0) {
throw new Exception("Should not be zero");
}
SampleField = sampleField;
}
}
Expected behavior
Deserialization exception is thrown:
Required property ‘SampleField’ not found in JSON. Path ‘’, line 1, position 2.
Actual behavior
User exception is thrown:
Should not be zero
Steps to reproduce
JsonConvert.DeserializeObject<SampleType>("{}");
Issue Analytics
- State:
- Created 4 years ago
- Comments:8
Top Results From Across the Web
Serialize/deserialize objects - order of fields matters?
The classes that I try to serialize/deserialize do not have order-attributes placed on fields/properties. Yet one of my fields always gets ...
Read more >JSON error when deserializing data
Hello all-. I'm getting the following when trying to deserialize a previously serialized object: System.JSONException: Invalid format: ...
Read more >Jackson – Decide What Fields Get Serialized/Deserialized
The simplest way to make sure a field is both serializable and deserializable is to make it public. Let's declare a simple class...
Read more >Skip invalid elements in a sequence with Serde deserialization
Using Serde, I would like to Deserialize a sequence of elements by keeping the valid elements and skip the invalid ones.
Read more >Migrate from Newtonsoft.Json to System.Text.Json - .NET
Newtonsoft.Json can serialize and deserialize fields as well as properties. In System.Text.Json, use the JsonSerializerOptions.IncludeFields ...
Read more >
Top Related Medium Post
No results found
Top Related StackOverflow Question
No results found
Troubleshoot Live Code
Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free
Top Related Reddit Thread
No results found
Top Related Hackernoon Post
No results found
Top Related Tweet
No results found
Top Related Dev.to Post
No results found
Top Related Hashnode Post
No results found
Json.NET seems to be working as designed here. Two points:
When deserializing using a parameterized constructor, Json.NET will automatically provide a default value for any constructor arguments without matching JSON properties.
This is described in, e.g. this answer to Newtonsoft json deserialise missing int values as nulls instead of zero and this answer to How does JSON deserialization in C# work.
When the c# data model has required properties and one or more are missing, then a
JsonSerializationException
is thrown after deserialization is complete.When deserializing an object with a default constructor this falls out naturally from Json.NET’s streaming approach to deserialization. First the object is constructed. Next Json.NET streams through the JSON, deserializing and populating each property and tracking which ones were found. Finally, if a required property was not encountered, an exception is thrown at the end of the process.
It seems as though Newtonsoft made a conscious decision to preserve this order when deserializing objects with parameterized constructors. They did this despite the fact that it would have been possible to throw the exception at the beginning of deserialization, since the JSON gets preloaded and deserialized into property and argument values before the object is constructed.
To confirm this, see JsonSerializerInternalReader.CreateObjectUsingCreatorWithParameters(). Checking for missing properties is done in the call to
EndProcessProperty()
which happens is towards the end of the algorithm, just before calling finallyOnDeserialized
.If I modify your type as follows, so that validation of
sampleField
is done in anOnDeserialized
method and a factory method that checks for appropriate argument values replaces the public parameterized constructor:Then a
JsonSerializationException
is thrown as expected, rather than the custom exception. Demo fiddle here.I cannot, however, find anything in the official documentation that explains exactly how JSON properties are matched to constructor arguments, what happens when a constructor argument is missing, how and whether
[JsonProperty]
attributes applied to .Net properties affect the deserialization of similarly named constructor arguments, or when missing property validation occurrs. Those are all topics that Newtonsoft might want to clarify in their documentation.Apologies - seems I scanned too fast - I’m surprised it entered the constructor as you’re saying; I agree there is a case to be understood. But, the truth re these edge cases will definitely be in the tests…