Skip to content

Commit

Permalink
Fix deserialization of nullable structs with constructors. (#86896)
Browse files Browse the repository at this point in the history
  • Loading branch information
eiriktsarpalis authored May 30, 2023
1 parent 236ce6a commit bf7fb2e
Show file tree
Hide file tree
Showing 5 changed files with 31 additions and 0 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ internal sealed class NullableConverter<T> : JsonConverter<T?> where T : struct
internal override Type? ElementType => typeof(T);
public override bool HandleNull => true;
internal override bool CanPopulate => _elementConverter.CanPopulate;
internal override bool ConstructorIsParameterized => _elementConverter.ConstructorIsParameterized;

// It is possible to cache the underlying converter since this is an internal converter and
// an instance is created only once for each JsonSerializerOptions instance.
Expand All @@ -20,6 +21,7 @@ public NullableConverter(JsonConverter<T> elementConverter)
_elementConverter = elementConverter;
IsInternalConverterForNumberType = elementConverter.IsInternalConverterForNumberType;
ConverterStrategy = elementConverter.ConverterStrategy;
ConstructorInfo = elementConverter.ConstructorInfo;
}

internal override bool OnTryRead(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options, scoped ref ReadStack state, out T? value)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -160,5 +160,16 @@ public async Task Struct_Use_DefaultCtor_ByDefault()
Assert.Equal(1, point2.X);
Assert.Equal(2, point2.Y);
}

[Fact]
public async Task CanDeserializeNullableStructWithCtor()
{
string json = @"{""X"":1,""Y"":2}";

Point_2D_Struct_WithAttribute? point2 = await Serializer.DeserializeWrapper<Point_2D_Struct_WithAttribute?>(json);
Assert.NotNull(point2);
Assert.Equal(1, point2.Value.X);
Assert.Equal(2, point2.Value.Y);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,20 @@ public virtual async Task NonPublicInitOnlySetter_With_JsonInclude(Type type)
Assert.Equal(@"{""MyInt"":1}", await Serializer.SerializeWrapper(obj));
}

[Fact]
public async Task NullableStructWithInitOnlyProperty()
{
// Regression test for https://github.com/dotnet/runtime/issues/86483

StructWithInitOnlyProperty? value = new StructWithInitOnlyProperty { MyInt = 42 };
string json = await Serializer.SerializeWrapper(value);

Assert.Equal("""{"MyInt":42}""", json);

StructWithInitOnlyProperty? deserializedValue = await Serializer.DeserializeWrapper<StructWithInitOnlyProperty?>(json);
Assert.Equal(deserializedValue, value);
}

public class ClassWithInitOnlyProperty
{
public int MyInt { get; init; }
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,7 @@ protected ConstructorTests_Metadata(JsonSerializerWrapper stringWrapper)
[JsonSerializable(typeof(Parameterized_WrapperForICollection))]
[JsonSerializable(typeof(Point_2D_Struct))]
[JsonSerializable(typeof(Point_2D_Struct_WithAttribute))]
[JsonSerializable(typeof(Point_2D_Struct_WithAttribute?))]
[JsonSerializable(typeof(ObjWCtorMixedParams))]
[JsonSerializable(typeof(Person_Class))]
[JsonSerializable(typeof(Point_2D))]
Expand Down Expand Up @@ -200,6 +201,7 @@ public ConstructorTests_Default(JsonSerializerWrapper jsonSerializer) : base(jso
[JsonSerializable(typeof(Parameterized_WrapperForICollection))]
[JsonSerializable(typeof(Point_2D_Struct))]
[JsonSerializable(typeof(Point_2D_Struct_WithAttribute))]
[JsonSerializable(typeof(Point_2D_Struct_WithAttribute?))]
[JsonSerializable(typeof(ObjWCtorMixedParams))]
[JsonSerializable(typeof(Person_Class))]
[JsonSerializable(typeof(Point_2D))]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -162,6 +162,7 @@ public override async Task HonorJsonPropertyName_PrivateSetter()
[JsonSerializable(typeof(ClassWithMissingObjectProperty))]
[JsonSerializable(typeof(ClassWithInitOnlyProperty))]
[JsonSerializable(typeof(StructWithInitOnlyProperty))]
[JsonSerializable(typeof(StructWithInitOnlyProperty?))]
[JsonSerializable(typeof(ClassWithCustomNamedInitOnlyProperty))]
[JsonSerializable(typeof(StructWithCustomNamedInitOnlyProperty))]
[JsonSerializable(typeof(MyClassWithValueTypeInterfaceProperty))]
Expand Down Expand Up @@ -406,6 +407,7 @@ public void PublicContextAndJsonSerializerOptions()
[JsonSerializable(typeof(ClassWithMissingObjectProperty))]
[JsonSerializable(typeof(ClassWithInitOnlyProperty))]
[JsonSerializable(typeof(StructWithInitOnlyProperty))]
[JsonSerializable(typeof(StructWithInitOnlyProperty?))]
[JsonSerializable(typeof(ClassWithCustomNamedInitOnlyProperty))]
[JsonSerializable(typeof(StructWithCustomNamedInitOnlyProperty))]
[JsonSerializable(typeof(MyClassWithValueTypeInterfaceProperty))]
Expand Down

0 comments on commit bf7fb2e

Please sign in to comment.