27

In Json.NET, how do I make ALL properties required upon deserialization? I know that I can do this with attributes on the messages, but I don't want to do that. Mainly because it would require my message library to take on an external dependency.

I tried the MissingMemberHandling.Error setting, but it does the opposite of what I want. I'm okay with the JSON having extra properties. I want it to fail when any target object properties are missing in the JSON.

I'm actually deserializing to F# records, and the properties can't ordinarily be null anyway. (They can't be assigned null by normal means in code.) But Json.NET happily defaults properties to null under the covers when data is missing.


F# version of accepted answer

Resolver

open System
open Newtonsoft.Json
open Newtonsoft.Json.Serialization

type RequireAllPropertiesContractResolver() =
    inherit DefaultContractResolver()

    override me.CreateObjectContract(objectType:Type) =
        let contract = base.CreateObjectContract(objectType)
        contract.ItemRequired <- new Nullable<Required>(Required.Always)
        contract

In the settings

let settings = new JsonSerializerSettings() // default settings
...
settings.ContractResolver <- new RequireAllPropertiesContractResolver()
Kasey Speakman
  • 4,511
  • 2
  • 32
  • 41

2 Answers2

40

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:

dbc
  • 104,963
  • 20
  • 228
  • 340
  • Adding an attribute to all class and their subclasses is a no-go. At that point, it's easier just to wrap the call that uses the data in a try/catch for null reference exceptions, which is what I am currently forced to do instead of catch it at the source. – Kasey Speakman Apr 15 '15 at 20:47
  • Maybe I misunderstood your question, I thought you objected to wrapping all properties? – dbc Apr 15 '15 at 20:48
  • Tagging all classes is a bit less work than all properties, but I would have to take a dependency on Json.NET for my message library, which currently has no other dependencies. Also F# records cannot inherit from base classes. I really need a way to force Json.NET to do this. – Kasey Speakman Apr 15 '15 at 20:52
  • 1
    This worked perfect for me, do you know if there is a way to get a list of all the missing properties in one error? right now I need to run it, find first error, fix, and then keep going which is tedious – TWilly Dec 08 '17 at 16:25
  • @TWilly - haven't tested it, but you may be able to handle the exception, log the error and continue as shown in https://www.newtonsoft.com/json/help/html/SerializationErrorHandling.htm. Then you'd have a list of all the errors. – dbc Dec 08 '17 at 17:45
  • 1
    Might be worth noting that this works both ways, meaning that serialization will fail if an object has a null property. I'm struggling to figure out how to have different ContractResolvers for serializing and deserializing in an ASP.NET Core 2 Web API. – Matt Jenkins Mar 11 '19 at 16:39
  • @noelicus - can you share a [mcve]? E.g. it won't work if there is a `JsonConverter` applied, in that case the converter itself is expected to take care of everything. – dbc Sep 09 '20 at 15:51
  • I appreciate you asking, but I've deleted what I did now. it was a copy and paste of the above and I didn't set the converter. The settings obj only had ContractResolver set. You would expect this to work on Deserialize despite the name? – noelicus Sep 09 '20 at 16:15
  • Done: https://stackoverflow.com/questions/63825826/how-to-override-the-required-always-in-newtonsoft-json – noelicus Sep 10 '20 at 08:33
  • Aware that this was written before Nullable, but it doesn't work with nullable fields – Yarek T Mar 31 '22 at 14:13
  • So after some research I switched back to default contract resolver, and adding `[property:JsonRequired]` onto records manually. I know that doesn't answer OP though. Would still like to see a solution that works without manual annotation for times where you can't do that. – Yarek T Mar 31 '22 at 15:03
  • 1
    @YarekT - maybe you should ask a separate question with a [mcve] then. This was written for f# which never wants null reference values, maybe you need something more general. – dbc Apr 04 '22 at 02:29
3

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.

Dzoukr
  • 1,094
  • 1
  • 13
  • 17
  • Sounds similar to [Json.NET make property required based on property type](https://stackoverflow.com/a/48330214/3744182). – dbc Sep 06 '18 at 17:40
  • Yes, you are right. I don't know why I was not able to find this answer after hours of googling. :) – Dzoukr Sep 08 '18 at 06:48