Home > Back-end >  Throw exception when missing not nullable value in System.Text.Json
Throw exception when missing not nullable value in System.Text.Json

Time:11-19

Having a <nullable>enabled</nullable> in project settings, having the following class

public class Car
{
    public required string Name { get; init; }
}

and deserializing it from string:

System.Text.Json.JsonSerializer.Deserialize<Car>("""{"Name": null}""");

Does not throw an exception

Because property is marked as not nullable, is it possible to configure STJ to throw in case of null value?

CodePudding user response:

Since you are using .NET 7 and C# 11 by the looks from required modifier, you could try using JsonRequiredAttribute from System.Text.Json package.

Alternatively there is a similar option in Newtonsoft.Json package.

public class Car
{
    [JsonProperty(Required = Required.Always)]
    public string Name { get; init; }
}

CodePudding user response:

required as JsonRequiredAttribute results in JsonTypeInfo.IsRequired set to true and have same behaviour - throws only if the corresponding property is not present in the json (docs):

Starting in .NET 7, you can mark certain properties to signify that they must be present in the JSON payload for deserialization to succeed.

If you want to catch the properties being set to null you can leverage the ability to customize the serialization contract. You can start from the following:

private static void ThrowNullableRequired(JsonTypeInfo jsonTypeInfo)
{
    if (jsonTypeInfo.Kind != JsonTypeInfoKind.Object)
    {
        return;
    }

    foreach (var property in jsonTypeInfo.Properties)
    {
        if (property.IsRequired && !property.PropertyType.IsValueType && property.Set is { } setter)
        {
            // not sure maybe should cache nullability checks
            NullabilityInfoContext context = new();
            var nullabilityInfo = context.Create(jsonTypeInfo.Type.GetProperty(property.Name));
            if (nullabilityInfo.WriteState is not NullabilityState.NotNull)
            {
                continue;
            }

            property.Set = (obj, val) =>
            {
                if (val is null)
                {
                    throw new JsonException($"null for required prop: {property.Name}");
                }

                setter(obj, val);
            };
        }
    }
}

And usage:

var settings = new JsonSerializerOptions
{
    TypeInfoResolver = new DefaultJsonTypeInfoResolver
    {
        Modifiers = { ThrowNullableRequired }
    }
};
    
try
{
    var myClass1 = JsonSerializer.Deserialize<Car>(
        """
        {
            "Name":null,
            "Prefix":null
        }
        """, settings);
}
catch (Exception e)
{
}

var car = JsonSerializer.Deserialize<Car>(
    """
        {
            "Name":"a",
            "Prefix":null
        }
        """, settings);
  • Related