Skip to content

Commit bf7fb2e

Browse files
Fix deserialization of nullable structs with constructors. (#86896)
1 parent 236ce6a commit bf7fb2e

File tree

5 files changed

+31
-0
lines changed

5 files changed

+31
-0
lines changed

src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/Value/NullableConverter.cs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ internal sealed class NullableConverter<T> : JsonConverter<T?> where T : struct
1010
internal override Type? ElementType => typeof(T);
1111
public override bool HandleNull => true;
1212
internal override bool CanPopulate => _elementConverter.CanPopulate;
13+
internal override bool ConstructorIsParameterized => _elementConverter.ConstructorIsParameterized;
1314

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

2527
internal override bool OnTryRead(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options, scoped ref ReadStack state, out T? value)

src/libraries/System.Text.Json/tests/Common/ConstructorTests/ConstructorTests.AttributePresence.cs

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -160,5 +160,16 @@ public async Task Struct_Use_DefaultCtor_ByDefault()
160160
Assert.Equal(1, point2.X);
161161
Assert.Equal(2, point2.Y);
162162
}
163+
164+
[Fact]
165+
public async Task CanDeserializeNullableStructWithCtor()
166+
{
167+
string json = @"{""X"":1,""Y"":2}";
168+
169+
Point_2D_Struct_WithAttribute? point2 = await Serializer.DeserializeWrapper<Point_2D_Struct_WithAttribute?>(json);
170+
Assert.NotNull(point2);
171+
Assert.Equal(1, point2.Value.X);
172+
Assert.Equal(2, point2.Value.Y);
173+
}
163174
}
164175
}

src/libraries/System.Text.Json/tests/Common/PropertyVisibilityTests.InitOnly.cs

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,20 @@ public virtual async Task NonPublicInitOnlySetter_With_JsonInclude(Type type)
6464
Assert.Equal(@"{""MyInt"":1}", await Serializer.SerializeWrapper(obj));
6565
}
6666

67+
[Fact]
68+
public async Task NullableStructWithInitOnlyProperty()
69+
{
70+
// Regression test for https://github.com/dotnet/runtime/issues/86483
71+
72+
StructWithInitOnlyProperty? value = new StructWithInitOnlyProperty { MyInt = 42 };
73+
string json = await Serializer.SerializeWrapper(value);
74+
75+
Assert.Equal("""{"MyInt":42}""", json);
76+
77+
StructWithInitOnlyProperty? deserializedValue = await Serializer.DeserializeWrapper<StructWithInitOnlyProperty?>(json);
78+
Assert.Equal(deserializedValue, value);
79+
}
80+
6781
public class ClassWithInitOnlyProperty
6882
{
6983
public int MyInt { get; init; }

src/libraries/System.Text.Json/tests/System.Text.Json.SourceGeneration.Tests/Serialization/ConstructorTests.cs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,7 @@ protected ConstructorTests_Metadata(JsonSerializerWrapper stringWrapper)
5858
[JsonSerializable(typeof(Parameterized_WrapperForICollection))]
5959
[JsonSerializable(typeof(Point_2D_Struct))]
6060
[JsonSerializable(typeof(Point_2D_Struct_WithAttribute))]
61+
[JsonSerializable(typeof(Point_2D_Struct_WithAttribute?))]
6162
[JsonSerializable(typeof(ObjWCtorMixedParams))]
6263
[JsonSerializable(typeof(Person_Class))]
6364
[JsonSerializable(typeof(Point_2D))]
@@ -200,6 +201,7 @@ public ConstructorTests_Default(JsonSerializerWrapper jsonSerializer) : base(jso
200201
[JsonSerializable(typeof(Parameterized_WrapperForICollection))]
201202
[JsonSerializable(typeof(Point_2D_Struct))]
202203
[JsonSerializable(typeof(Point_2D_Struct_WithAttribute))]
204+
[JsonSerializable(typeof(Point_2D_Struct_WithAttribute?))]
203205
[JsonSerializable(typeof(ObjWCtorMixedParams))]
204206
[JsonSerializable(typeof(Person_Class))]
205207
[JsonSerializable(typeof(Point_2D))]

src/libraries/System.Text.Json/tests/System.Text.Json.SourceGeneration.Tests/Serialization/PropertyVisibilityTests.cs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -162,6 +162,7 @@ public override async Task HonorJsonPropertyName_PrivateSetter()
162162
[JsonSerializable(typeof(ClassWithMissingObjectProperty))]
163163
[JsonSerializable(typeof(ClassWithInitOnlyProperty))]
164164
[JsonSerializable(typeof(StructWithInitOnlyProperty))]
165+
[JsonSerializable(typeof(StructWithInitOnlyProperty?))]
165166
[JsonSerializable(typeof(ClassWithCustomNamedInitOnlyProperty))]
166167
[JsonSerializable(typeof(StructWithCustomNamedInitOnlyProperty))]
167168
[JsonSerializable(typeof(MyClassWithValueTypeInterfaceProperty))]
@@ -406,6 +407,7 @@ public void PublicContextAndJsonSerializerOptions()
406407
[JsonSerializable(typeof(ClassWithMissingObjectProperty))]
407408
[JsonSerializable(typeof(ClassWithInitOnlyProperty))]
408409
[JsonSerializable(typeof(StructWithInitOnlyProperty))]
410+
[JsonSerializable(typeof(StructWithInitOnlyProperty?))]
409411
[JsonSerializable(typeof(ClassWithCustomNamedInitOnlyProperty))]
410412
[JsonSerializable(typeof(StructWithCustomNamedInitOnlyProperty))]
411413
[JsonSerializable(typeof(MyClassWithValueTypeInterfaceProperty))]

0 commit comments

Comments
 (0)