Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions src/libraries/System.Text.Json/src/Resources/Strings.resx
Original file line number Diff line number Diff line change
Expand Up @@ -764,6 +764,9 @@
<data name="JsonSchemaExporter_DepthTooLarge" xml:space="preserve">
<value>The depth of the generated JSON schema exceeds the JsonSerializerOptions.MaxDepth setting.</value>
</data>
<data name="JsonPropertyRequiredAndCollectionPropertyMissingGetter" xml:space="preserve">
<value>JsonPropertyInfo '{0}' defined in type '{1}' is marked required but is a collection property and does not specify a getter.</value>
</data>
<!-- System.Collections polyfills -->
<data name="Arg_WrongType" xml:space="preserve">
<value>The value '{0}' is not of type '{1}' and cannot be used in this generic collection.</value>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -452,6 +452,11 @@ internal void Configure()
!(AssociatedParameter?.IsRequiredParameter is true &&
Options.RespectRequiredConstructorParameters))
{
if ((EffectiveConverter.ConverterStrategy & (ConverterStrategy.Enumerable | ConverterStrategy.Dictionary)) != 0 &&
Set != null && !_isUserSpecifiedSetter)
{
ThrowHelper.ThrowInvalidOperationException_JsonPropertyRequiredAndCollectionPropertyMissingGetter(this);
}
ThrowHelper.ThrowInvalidOperationException_JsonPropertyRequiredAndNotDeserializable(this);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -275,6 +275,12 @@ public static void ThrowInvalidOperationException_SerializerPropertyNameNull(Jso
throw new InvalidOperationException(SR.Format(SR.SerializerPropertyNameNull, jsonPropertyInfo.DeclaringType, jsonPropertyInfo.MemberName));
}

[DoesNotReturn]
public static void ThrowInvalidOperationException_JsonPropertyRequiredAndCollectionPropertyMissingGetter(JsonPropertyInfo jsonPropertyInfo)
{
throw new InvalidOperationException(SR.Format(SR.JsonPropertyRequiredAndCollectionPropertyMissingGetter, jsonPropertyInfo.Name, jsonPropertyInfo.DeclaringType));
}

[DoesNotReturn]
public static void ThrowInvalidOperationException_JsonPropertyRequiredAndNotDeserializable(JsonPropertyInfo jsonPropertyInfo)
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -610,6 +610,23 @@ public class ClassWithRequiredExtensionDataProperty
public required Dictionary<string, JsonElement>? TestExtensionData { get; set; }
}

[Fact]
public async Task RequiredCollectionPropertyWithoutGetterThrows()
{
string json = """{"Foo":"foo","Bar":"bar"}""";
InvalidOperationException exception = await Assert.ThrowsAsync<InvalidOperationException>(
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think this is the correct way to address the problem. A fix should instead try to make the scenario work as expected. If you remove JsonRequired from the repro in #104700 then deserialization works as expected and this should too.

async () => await Serializer.DeserializeWrapper<ClassWithRequiredCollectionPropertyWithoutGetter>(json));
Assert.Contains(nameof(ClassWithRequiredCollectionPropertyWithoutGetter.TestCollectionPropertyWithoutGetter), exception.Message);
}

public class ClassWithRequiredCollectionPropertyWithoutGetter
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just to make sure we're not regressing things here, could you also add a test for class like so:

class ClassWithCollectionCtorParam(Dictionary<string, JsonElement> value)
{
    public Dictionary<string, JsonElement> Value { get; } = value;
}

And then try to deserialize it using an options instance with RespectRequiredConstructorParameters set to true.

{
public required Dictionary<string, JsonElement>? TestCollectionPropertyWithoutGetter
{
set => TestCollectionPropertyWithoutGetter = value;
}
}

[Fact]
public async Task RequiredKeywordAndJsonRequiredCustomAttributeWorkCorrectlyTogether()
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ public RequiredKeywordTests_SourceGen()
[JsonSerializable(typeof(ClassWithInitOnlyRequiredProperty))]
[JsonSerializable(typeof(ClassWithRequiredField))]
[JsonSerializable(typeof(ClassWithRequiredExtensionDataProperty))]
[JsonSerializable(typeof(ClassWithRequiredCollectionPropertyWithoutGetter))]
[JsonSerializable(typeof(ClassWithRequiredKeywordAndJsonRequiredCustomAttribute))]
[JsonSerializable(typeof(ClassWithCustomRequiredPropertyName))]
[JsonSerializable(typeof(DerivedClassWithRequiredInitOnlyProperty))]
Expand Down