When Json.NET encounters an object without a constructor without parameters, but with a parameterized constructor, it calls this constructor to create an object that matches the JSON property names with the constructor arguments by name, using reflection using a case-sensitive best match algorithm. That is, a property whose name also appears in the constructor will be set through a call to the constructor, and not the specified method (even if it exists).
That way, you can mark the constructor argument as required by marking the equivalent property as required :
public class Dog { public Dog(int age) { this.Age = age; } [JsonProperty(Required = Required.Always)] public int Age { get; } }
Now JsonConvert.DeserializeObject<Dog>(jsonString) will JsonConvert.DeserializeObject<Dog>(jsonString) when the "age" property is missing.
Since this is what you always want, you can create a custom contract converter that inherits from DefaultContractResolver or CamelCasePropertyNamesContractResolver , which automatically marks the properties passed to the constructor, without the need for attributes
public class ConstructorParametersRequiredContractResolver : DefaultContractResolver { // As of 7.0.1, Json.NET suggests using a static instance for "stateless" contract resolvers, for performance reasons. // http://www.newtonsoft.com/json/help/html/ContractResolver.htm // http://www.newtonsoft.com/json/help/html/M_Newtonsoft_Json_Serialization_DefaultContractResolver__ctor_1.htm // "Use the parameterless constructor and cache instances of the contract resolver within your application for optimal performance." // See also https://stackoverflow.com/questions/33557737/does-json-net-cache-types-serialization-information static ConstructorParametersRequiredContractResolver instance; static ConstructorParametersRequiredContractResolver() { instance = new ConstructorParametersRequiredContractResolver(); } public static ConstructorParametersRequiredContractResolver Instance { get { return instance; } } protected override JsonProperty CreatePropertyFromConstructorParameter(JsonProperty matchingMemberProperty, ParameterInfo parameterInfo) { var property = base.CreatePropertyFromConstructorParameter(matchingMemberProperty, parameterInfo); if (property != null && matchingMemberProperty != null) { var required = matchingMemberProperty.Required; if (required == Required.Default) { if (matchingMemberProperty.PropertyType != null && (matchingMemberProperty.PropertyType.IsValueType && Nullable.GetUnderlyingType(matchingMemberProperty.PropertyType) == null)) { required = Required.Always; } else { required = Required.AllowNull; } // It turns out to be necessary to mark the original matchingMemberProperty as required. property.Required = matchingMemberProperty.Required = required; } } return property; } }
Then
var settings = new JsonSerializerSettings { ContractResolver = ConstructorParametersRequiredContractResolver.Instance }; JsonConvert.DeserializeObject<T>(jsonString, settings)
Give it up again.
(Note that this only works if there is a corresponding property. There is no direct way to mark a constructor parameter without the corresponding property, if required.)
dbc
source share