CanBePolymorphic = TypeToConvert == typeof(object);
IsValueType = TypeToConvert.IsValueType;
HandleNull = IsValueType;
+ CanBeNull = !IsValueType || Nullable.GetUnderlyingType(TypeToConvert) != null;
IsInternalConverter = GetType().Assembly == typeof(JsonConverter).Assembly;
CanUseDirectReadOrWrite = !CanBePolymorphic && IsInternalConverter && ClassType == ClassType.Value;
}
/// </remarks>
public virtual bool HandleNull { get; }
+ /// <summary>
+ /// Can <see langword="null"/> be assigned to <see cref="TypeToConvert"/>?
+ /// </summary>
+ internal bool CanBeNull { get; }
+
/// <summary>
/// Is the converter built-in.
/// </summary>
// For perf and converter simplicity, handle null here instead of forwarding to the converter.
if (reader.TokenType == JsonTokenType.Null && !HandleNull)
{
+ if (!CanBeNull)
+ {
+ ThrowHelper.ThrowJsonException_DeserializeUnableToConvertValue(TypeToConvert);
+ }
+
value = default;
return true;
}
{
if (reader.TokenType == JsonTokenType.Null && !HandleNull && !wasContinuation)
{
+ if (!CanBeNull)
+ {
+ ThrowHelper.ThrowJsonException_DeserializeUnableToConvertValue(TypeToConvert);
+ }
+
// For perf and converter simplicity, handle null here instead of forwarding to the converter.
value = default;
success = true;
// For perf and converter simplicity, handle null here instead of forwarding to the converter.
if (reader.TokenType == JsonTokenType.Null && !HandleNull)
{
+ if (!CanBeNull)
+ {
+ ThrowHelper.ThrowJsonException_DeserializeUnableToConvertValue(TypeToConvert);
+ }
+
value = default;
state.Pop(true);
return true;
if (isNullToken && !_converter.HandleNull && !state.IsContinuation)
{
+ if (!_converter.CanBeNull)
+ {
+ ThrowHelper.ThrowJsonException_DeserializeUnableToConvertValue(_converter.TypeToConvert);
+ }
+
// Don't have to check for IgnoreNullValue option here because we set the default value regardless.
value = DefaultValue;
return true;
if (isNullToken && !_converter.HandleNull && !state.IsContinuation)
{
+ if (!_converter.CanBeNull)
+ {
+ ThrowHelper.ThrowJsonException_DeserializeUnableToConvertValue(_converter.TypeToConvert);
+ }
+
// Don't have to check for IgnoreNullValue option here because we set the default value regardless.
value = TypedDefaultValue;
return true;
{
if (!IgnoreNullValues)
{
+ if (!Converter.CanBeNull)
+ {
+ ThrowHelper.ThrowJsonException_DeserializeUnableToConvertValue(Converter.TypeToConvert);
+ }
+
T value = default;
Set!(obj, value!);
}
bool isNullToken = reader.TokenType == JsonTokenType.Null;
if (isNullToken && !Converter.HandleNull && !state.IsContinuation)
{
+ if (!Converter.CanBeNull)
+ {
+ ThrowHelper.ThrowJsonException_DeserializeUnableToConvertValue(Converter.TypeToConvert);
+ }
+
value = default(T)!;
success = true;
}
var options = new JsonSerializerOptions();
options.Converters.Add(new Int32NullConverter_OptOut());
- // Serializer sets default value (doesn't fallback to built-in converter).
- Assert.Equal(0, JsonSerializer.Deserialize<int>("null", options));
+ // Serializer throws JsonException if null is assigned to value that can't be null.
+ Assert.Throws<JsonException>(() => JsonSerializer.Deserialize<int>("null", options));
}
private class Int32NullConverter_OptOut : Int32NullConverter_SpecialCaseNull
var options = new JsonSerializerOptions();
options.Converters.Add(new PointStructConverter_OptOut());
- var obj = JsonSerializer.Deserialize<Point_2D_Struct>("null", options);
- Assert.Equal(0, obj.X);
- Assert.Equal(0, obj.Y);
+ Assert.Throws<JsonException>(() => JsonSerializer.Deserialize<Point_2D_Struct>("null", options));
}
private class PointStructConverter_OptOut : PointStructConverter_SpecialCaseNull