New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
The C# required
modifier should trigger Required.Always
behaviour
#2918
Comments
I hope this project doesn't make the same design mistake that dotnet made. The For example, you could have your type defined like so for the purpose of ensuring its set in object initializers: public class Thing
{
public required string? Field { get; init; }
} ...but System.Text.Json will now throw an error when The main problem described in this issue is that a nullable value has been assigned to a non-nullable one and I think it would be nice to have better solutions around that instead. Then for marking a property as required for JSON deserialization, I believe it should be kept separate from the public class Thing
{
[JsonRequired] // required for JSON deserialization
public required string? Field { get; init; } // required in object initializers
} |
I disagree. Default values are, in my view, a disaster of a language concept. C# is stuck with them for backward compatibility, but for the first time with the required keyword, we have a way to express to the type system that something must always have a meaningful value. The benefit of being able to rely on this without exception is huge, and can eliminate a large class of bugs. However, if there are common circumstances (such as when using this library), where the apparent guarantee of the required keyword is broken, then it significantly undermines this. I don't know what the language designers had in mind for this keyword, but it seems to me far more useful if libraries such as this take the approach that I'm suggesting. It can always be optional configuration so that those like me that never want to see another number come through as 0 when it was actually misspelt can have our wish. On the null issue, I don't really see the problem, because things can be explicitly set to null in json just as they can in an object initialiser. If they're missing, that usually indicates a problem, not a legitimate null value. |
It's very common for null values to be omitted in serialized JSON in order to use less data. If the
I disagree, this keyword was intended to be a way to mark a property as required in an object initializer. Now libraries (like System.Text.Json) are making it have additional meanings. Nullable types should be used to help enforce a non-nullable value and not the
The ideal behaviour for a C# deserialization library would be for a Another point to note is that making the |
I absolutely 100% agree that System.Text.Json should not have given runtime significance to what is a essentially a compile time feature. I have excluded serializing default values many times as a conscientious design choice.
Just read this and this. I believe it's very clear what the spirit and extent of the feature would be. Recently I made use of the new
The problem is backwards compatibility with stored JSON or JSON coming from an external system that we may have absolutely no control whatsoever.
I understand, but the safest way to deal with distaste of default values is to use a language where that concept is not baked into its foundations. |
To @dsherret's:
I could not agree with you more! 🤓 After having got perhaps a bit too giddy with "required but optional (nullable)" makes no sense in the JSON context, especially when using |
@QuintinWillison I found a way to disable it for System.Text.Json here by overriding the Code from linkvar resolver = = new DefaultJsonTypeInfoResolver
{
Modifiers =
{
static typeInfo =>
{
foreach (var info in typeInfo.Properties)
{
if (info.IsRequired)
{
info.IsRequired = info.AttributeProvider?.IsDefined(
typeof(JsonRequiredAttribute),
inherit: false
) ?? false;
}
}
}
}
};
var result = JsonSerializer.Deserialize<MyRecord>(
"{}",
new JsonSerializerOptions
{
TypeInfoResolver = resolver,
}
);
// or for asp.net core
builder.Services.AddControllers().AddJsonOptions(options =>
{
options.JsonSerializerOptions.TypeInfoResolver = resolver
}); |
@QuintinWillison @dsherret public static JsonSerializerOptions IgnoreCSharpRequiredModifiers(this JsonSerializerOptions options)
{
options.TypeInfoResolver ??= new DefaultJsonTypeInfoResolver();
options.TypeInfoResolver =
options.TypeInfoResolver.WithAddedModifier(static typeInfo => typeInfo.IgnoreCSharpRequiredModifiers());
return options;
}
private static void IgnoreCSharpRequiredModifiers(this JsonTypeInfo info)
{
foreach (var p in info.Properties)
p.IsRequired &= p.AttributeProvider.HasJsonRequiredAttribute();
}
private static bool HasJsonRequiredAttribute(this ICustomAttributeProvider? attributes) =>
attributes is not null && attributes.IsDefined(typeof(JsonRequiredAttribute), inherit: false); |
@tobymiller - Its a bad assumption that the value will be meaningful and not just some unexpected/garbage. Is this the default value disaster of which you speak? pubic class MyClass
{
public Dictionary<string, string> HumanDictionary {get; } = new(StringComparer.OrdinalIgnoreCase);
} This seems like a win to me. NullReferenceExceptions avoided when someone calls |
In C# 11 (dotnet 7) the
required
modifier was introduced for fields and properties. It forces a caller to provide a value in, for example, an object initialiser. The compiler enforces this, and knows not to complain about non-nullable types without a default value, as a value must always be provided.System.Text.Json
throws an exception if arequired
field is omitted in source json. However, Newtonsoft does not, and silently fills this field with a default value, such asnull
. The desired behaviour can be achieved in Newtonsoft usingJsonProperty(Required = Required.Always)
, but this is verbose and should be redundant in these cases.I think it would be sensible default behaviour (or at least configurable behaviour) to respect
required
, as intuitively, this is what is expected.Source/destination types
Source JSON
Expected behavior
An error to be thrown, as
Field
isrequired
.Actual behavior
Newtonsoft deserialises happily, with
null
being the value ofField
, even though this is invalid as the field is non-nullable.Steps to reproduce
The text was updated successfully, but these errors were encountered: