Json.NET require all properties on deserialization

Solution 1:

If your model has properties that your JSON may omit, and you want that to be an error, add the attribute [JsonObject(ItemRequired=Required.Always)] to your classes:

Type: Required

A value indicating whether the object's properties are required.

Possible values for Required are:

  • Default: The property is not required. The default state.
  • AllowNull: The property must be defined in JSON but can be a null value.
  • Always: The property must be defined in JSON and cannot be a null value.
  • DisallowNull: The property is not required but it cannot be a null value [if present]. (Json.NET 8.0.1 and later.)

The setting is inherited so can be added to a generic base class.

Update

To do it globally for all objects, subclass the DefaultContractResolver and add the ItemRequired flag to all object contracts:

public class RequireObjectPropertiesContractResolver : DefaultContractResolver
{
    protected override JsonObjectContract CreateObjectContract(Type objectType)
    {
        var contract = base.CreateObjectContract(objectType);
        contract.ItemRequired = Required.Always;
        return contract;
    }
}

And then later, in settings:

var settings = new JsonSerializerSettings { ContractResolver = new RequireObjectPropertiesContractResolver() };

Notes:

  • If you don't want to require JSON properties when your f# member is optional see this answer to this question and also the question Json.NET make property required based on property type.

  • This contract resolver applies a default setting of Required.Always to all object properties, but will not override JsonProperty.AttributeRequired when applied directly. If you need that, see e.g. How to override the "Required.Always" in newtonsoft json.

  • As stated in the question, the setting MissingMemberHandling = MissingMemberHandling.Error solves a complimentary problem: if your JSON may have properties that your model omits, and you want that to be an error, use MissingMemberHandling.Error. See: MissingMemberHandling setting.

  • You may want to cache the contract resolver for best performance.

Solution 2:

I know I am late on party here, but... Accepted answer forces all properties to be available, which could be not so good for case when your record contains Option types combining with NullValueHandling.Ignore parameter on JsonSerializerSettings. In that case you would require option type to be present, which is too limiting. We found this solution works for us:

type RequireAllPropertiesContractResolver() =
    inherit CamelCasePropertyNamesContractResolver()

    override __.CreateProperty(memb, serialization) =
        let prop = base.CreateProperty(memb, serialization)
        let isRequired = not (prop.PropertyType.IsGenericType && prop.PropertyType.GetGenericTypeDefinition() = typedefof<option<_>>)
        if isRequired then prop.Required <- Required.Always
        prop

I hope it helps someone.