Skip to content

Commit 5cffdfe

Browse files
Throw InvalidOperationException with contextual message for open generic converter on incompatible type
Co-authored-by: eiriktsarpalis <2813363+eiriktsarpalis@users.noreply.github.com>
1 parent 3e2fbae commit 5cffdfe

File tree

4 files changed

+34
-13
lines changed

4 files changed

+34
-13
lines changed

src/libraries/System.Text.Json/src/Resources/Strings.resx

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -411,6 +411,9 @@
411411
<data name="SerializationConverterOnAttributeNotCompatible" xml:space="preserve">
412412
<value>The converter specified on '{0}' is not compatible with the type '{1}'.</value>
413413
</data>
414+
<data name="SerializationConverterOnAttributeOpenGenericNotCompatible" xml:space="preserve">
415+
<value>The open generic converter type '{1}' specified on '{0}' cannot be instantiated because the target type is not a generic type with a matching number of type parameters.</value>
416+
</data>
414417
<data name="SerializationConverterOnAttributeInvalid" xml:space="preserve">
415418
<value>The converter specified on '{0}' does not derive from JsonConverter or have a public parameterless constructor.</value>
416419
</data>

src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Metadata/DefaultJsonTypeInfoResolver.Converters.cs

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -192,11 +192,17 @@ private static JsonConverter GetConverterFromAttribute(JsonConverterAttribute co
192192
// Handle open generic converter types (e.g., OptionConverter<> on Option<T>).
193193
// If the converter type is an open generic and the type to convert is a closed generic
194194
// with matching type arity, construct the closed converter type.
195-
if (converterType.IsGenericTypeDefinition &&
196-
typeToConvert.IsGenericType &&
197-
converterType.GetGenericArguments().Length == typeToConvert.GetGenericArguments().Length)
195+
if (converterType.IsGenericTypeDefinition)
198196
{
199-
converterType = converterType.MakeGenericType(typeToConvert.GetGenericArguments());
197+
if (typeToConvert.IsGenericType &&
198+
converterType.GetGenericArguments().Length == typeToConvert.GetGenericArguments().Length)
199+
{
200+
converterType = converterType.MakeGenericType(typeToConvert.GetGenericArguments());
201+
}
202+
else
203+
{
204+
ThrowHelper.ThrowInvalidOperationException_SerializationConverterOnAttributeOpenGenericNotCompatible(declaringType, memberInfo, converterType);
205+
}
200206
}
201207

202208
ConstructorInfo? ctor = converterType.GetConstructor(Type.EmptyTypes);

src/libraries/System.Text.Json/src/System/Text/Json/ThrowHelper.Serialization.cs

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -229,6 +229,18 @@ public static void ThrowInvalidOperationException_SerializationConverterOnAttrib
229229
throw new InvalidOperationException(SR.Format(SR.SerializationConverterOnAttributeNotCompatible, location, typeToConvert));
230230
}
231231

232+
[DoesNotReturn]
233+
public static void ThrowInvalidOperationException_SerializationConverterOnAttributeOpenGenericNotCompatible(Type classType, MemberInfo? memberInfo, Type converterType)
234+
{
235+
string location = classType.ToString();
236+
if (memberInfo != null)
237+
{
238+
location += $".{memberInfo.Name}";
239+
}
240+
241+
throw new InvalidOperationException(SR.Format(SR.SerializationConverterOnAttributeOpenGenericNotCompatible, location, converterType));
242+
}
243+
232244
[DoesNotReturn]
233245
public static void ThrowInvalidOperationException_SerializerOptionsReadOnly(JsonSerializerContext? context)
234246
{

src/libraries/System.Text.Json/tests/System.Text.Json.Tests/Serialization/CustomConverterTests/CustomConverterTests.Attribute.cs

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -504,12 +504,12 @@ public override void Write(Utf8JsonWriter writer, MyGenericWrapper<T> value, Jso
504504

505505
// Tests for type parameter arity mismatch
506506
[Fact]
507-
public static void GenericConverterAttribute_ArityMismatch_ThrowsArgumentException()
507+
public static void GenericConverterAttribute_ArityMismatch_ThrowsInvalidOperationException()
508508
{
509-
// The converter has two type parameters but the type has one
510-
// This throws ArgumentException because the arity doesn't match, so the converter type
511-
// remains an unbound generic and Activator.CreateInstance fails.
512-
Assert.Throws<ArgumentException>(() => JsonSerializer.Serialize(new TypeWithArityMismatch<int>()));
509+
// The converter has two type parameters but the type has one.
510+
// This throws InvalidOperationException with a contextual error message
511+
// because the arity doesn't match.
512+
Assert.Throws<InvalidOperationException>(() => JsonSerializer.Serialize(new TypeWithArityMismatch<int>()));
513513
}
514514

515515
[JsonConverter(typeof(ConverterWithTwoParams<,>))]
@@ -580,12 +580,12 @@ public override void Write(Utf8JsonWriter writer, DifferentType<T> value, JsonSe
580580

581581
// Tests for open generic converter on a non-generic type
582582
[Fact]
583-
public static void GenericConverterAttribute_OpenGenericOnNonGenericType_ThrowsArgumentException()
583+
public static void GenericConverterAttribute_OpenGenericOnNonGenericType_ThrowsInvalidOperationException()
584584
{
585585
// The converter is an open generic but the target type is not generic,
586-
// so the converter type cannot be instantiated (Activator.CreateInstance fails
587-
// because Type.ContainsGenericParameters is true).
588-
Assert.Throws<ArgumentException>(() => JsonSerializer.Serialize(new NonGenericTypeWithOpenGenericConverter()));
586+
// so the converter type cannot be instantiated. We throw InvalidOperationException
587+
// with a contextual error message.
588+
Assert.Throws<InvalidOperationException>(() => JsonSerializer.Serialize(new NonGenericTypeWithOpenGenericConverter()));
589589
}
590590

591591
[JsonConverter(typeof(OpenGenericConverterForNonGenericType<>))]

0 commit comments

Comments
 (0)