From fc2d297e7ebd58f38d2290094646ded94d96f8f3 Mon Sep 17 00:00:00 2001
From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com>
Date: Thu, 23 Apr 2026 11:43:03 +0000
Subject: [PATCH 01/12] Initial plan
From 574687672f6fb174f14ac04d2eccc252f9eed069 Mon Sep 17 00:00:00 2001
From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com>
Date: Thu, 23 Apr 2026 12:23:17 +0000
Subject: [PATCH 02/12] Add open generic derived type support in polymorphic
serialization
- Add TryResolveOpenGenericDerivedType with type unification to PolymorphicTypeResolver
- Resolve open generic derived types in PolymorphicTypeResolver constructor
- Add new error message for unresolvable open generic derived types
- Add SYSLIB1227 source generator diagnostic for invalid open generics
- Update existing test to expect success for now-valid generic scenario
Agent-Logs-Url: https://github.com/dotnet/runtime/sessions/f39c123c-a576-4d8d-9da4-b02cd81b6577
Co-authored-by: eiriktsarpalis <2813363+eiriktsarpalis@users.noreply.github.com>
---
docs/project/list-of-diagnostics.md | 2 +-
...onSourceGenerator.DiagnosticDescriptors.cs | 8 +
.../gen/JsonSourceGenerator.Parser.cs | 16 +-
.../gen/Resources/Strings.resx | 6 +
.../gen/Resources/xlf/Strings.cs.xlf | 10 +
.../gen/Resources/xlf/Strings.de.xlf | 10 +
.../gen/Resources/xlf/Strings.es.xlf | 10 +
.../gen/Resources/xlf/Strings.fr.xlf | 10 +
.../gen/Resources/xlf/Strings.it.xlf | 10 +
.../gen/Resources/xlf/Strings.ja.xlf | 10 +
.../gen/Resources/xlf/Strings.ko.xlf | 10 +
.../gen/Resources/xlf/Strings.pl.xlf | 10 +
.../gen/Resources/xlf/Strings.pt-BR.xlf | 10 +
.../gen/Resources/xlf/Strings.ru.xlf | 10 +
.../gen/Resources/xlf/Strings.tr.xlf | 10 +
.../gen/Resources/xlf/Strings.zh-Hans.xlf | 10 +
.../gen/Resources/xlf/Strings.zh-Hant.xlf | 10 +
.../src/Resources/Strings.resx | 5 +-
.../Metadata/PolymorphicTypeResolver.cs | 171 +++++++++++++++++-
.../Text/Json/ThrowHelper.Serialization.cs | 6 +
.../PolymorphicTests.CustomTypeHierarchies.cs | 5 +-
21 files changed, 338 insertions(+), 11 deletions(-)
diff --git a/docs/project/list-of-diagnostics.md b/docs/project/list-of-diagnostics.md
index 306e9f9d8f2e83..561f0710e4bcc2 100644
--- a/docs/project/list-of-diagnostics.md
+++ b/docs/project/list-of-diagnostics.md
@@ -272,7 +272,7 @@ The diagnostic id values reserved for .NET Libraries analyzer warnings are `SYSL
| __`SYSLIB1224`__ | Types annotated with JsonSerializableAttribute must be classes deriving from JsonSerializerContext. |
| __`SYSLIB1225`__ | Type includes ref like property, field or constructor parameter. |
| __`SYSLIB1226`__ | 'JsonIgnoreCondition.Always' is not valid on type-level 'JsonIgnoreAttribute' annotations. |
-| __`SYSLIB1227`__ | _`SYSLIB1220`-`SYSLIB1229` reserved for System.Text.Json.SourceGeneration._ |
+| __`SYSLIB1227`__ | Open generic derived type is not supported on a non-generic base type. |
| __`SYSLIB1228`__ | _`SYSLIB1220`-`SYSLIB1229` reserved for System.Text.Json.SourceGeneration._ |
| __`SYSLIB1229`__ | _`SYSLIB1220`-`SYSLIB1229` reserved for System.Text.Json.SourceGeneration._ |
| __`SYSLIB1230`__ | Deriving from a `GeneratedComInterface`-attributed interface defined in another assembly is not supported. |
diff --git a/src/libraries/System.Text.Json/gen/JsonSourceGenerator.DiagnosticDescriptors.cs b/src/libraries/System.Text.Json/gen/JsonSourceGenerator.DiagnosticDescriptors.cs
index a926b75a5c1631..168c5d311019cf 100644
--- a/src/libraries/System.Text.Json/gen/JsonSourceGenerator.DiagnosticDescriptors.cs
+++ b/src/libraries/System.Text.Json/gen/JsonSourceGenerator.DiagnosticDescriptors.cs
@@ -131,6 +131,14 @@ internal static class DiagnosticDescriptors
category: JsonConstants.SystemTextJsonSourceGenerationName,
defaultSeverity: DiagnosticSeverity.Warning,
isEnabledByDefault: true);
+
+ public static DiagnosticDescriptor OpenGenericDerivedTypeNotSupported { get; } = DiagnosticDescriptorHelper.Create(
+ id: "SYSLIB1227",
+ title: new LocalizableResourceString(nameof(SR.OpenGenericDerivedTypeNotSupportedTitle), SR.ResourceManager, typeof(FxResources.System.Text.Json.SourceGeneration.SR)),
+ messageFormat: new LocalizableResourceString(nameof(SR.OpenGenericDerivedTypeNotSupportedMessageFormat), SR.ResourceManager, typeof(FxResources.System.Text.Json.SourceGeneration.SR)),
+ category: JsonConstants.SystemTextJsonSourceGenerationName,
+ defaultSeverity: DiagnosticSeverity.Warning,
+ isEnabledByDefault: true);
}
}
}
diff --git a/src/libraries/System.Text.Json/gen/JsonSourceGenerator.Parser.cs b/src/libraries/System.Text.Json/gen/JsonSourceGenerator.Parser.cs
index 5428245c7ce1b1..0a736abe948b77 100644
--- a/src/libraries/System.Text.Json/gen/JsonSourceGenerator.Parser.cs
+++ b/src/libraries/System.Text.Json/gen/JsonSourceGenerator.Parser.cs
@@ -847,7 +847,21 @@ private void ProcessTypeCustomAttributes(
{
Debug.Assert(attributeData.ConstructorArguments.Length > 0);
var derivedType = (ITypeSymbol)attributeData.ConstructorArguments[0].Value!;
- EnqueueType(derivedType, typeToGenerate.Mode);
+
+ // Open generic derived types (e.g. typeof(Derived<>)) are resolved at runtime
+ // based on the constructed base type's type arguments.
+ // Skip enqueueing them for source generation.
+ if (derivedType is INamedTypeSymbol { IsUnboundGenericType: true })
+ {
+ if (typeToGenerate.Type is not INamedTypeSymbol { IsGenericType: true })
+ {
+ ReportDiagnostic(DiagnosticDescriptors.OpenGenericDerivedTypeNotSupported, typeToGenerate.Location, derivedType.ToDisplayString(), typeToGenerate.Type.ToDisplayString());
+ }
+ }
+ else
+ {
+ EnqueueType(derivedType, typeToGenerate.Mode);
+ }
if (!isPolymorphic && typeToGenerate.Mode == JsonSourceGenerationMode.Serialization)
{
diff --git a/src/libraries/System.Text.Json/gen/Resources/Strings.resx b/src/libraries/System.Text.Json/gen/Resources/Strings.resx
index 71ed2ca15e21bf..e06e5becec0600 100644
--- a/src/libraries/System.Text.Json/gen/Resources/Strings.resx
+++ b/src/libraries/System.Text.Json/gen/Resources/Strings.resx
@@ -207,4 +207,10 @@
The type '{0}' has been annotated with 'JsonIgnoreAttribute' using 'JsonIgnoreCondition.Always' which is not valid on type declarations. The attribute will be ignored.
+
+ Open generic derived type is not supported on a non-generic base type.
+
+
+ The open generic type '{0}' cannot be used as a derived type for the non-generic polymorphic type '{1}'. The base type must be generic to resolve open generic derived types.
+
diff --git a/src/libraries/System.Text.Json/gen/Resources/xlf/Strings.cs.xlf b/src/libraries/System.Text.Json/gen/Resources/xlf/Strings.cs.xlf
index 79b8cd0d6dd8ba..93d723f8ecc4ac 100644
--- a/src/libraries/System.Text.Json/gen/Resources/xlf/Strings.cs.xlf
+++ b/src/libraries/System.Text.Json/gen/Resources/xlf/Strings.cs.xlf
@@ -132,6 +132,16 @@
Typ obsahuje více členů s komentářem JsonExtensionDataAttribute
+
+ The open generic type '{0}' cannot be used as a derived type for the non-generic polymorphic type '{1}'. The base type must be generic to resolve open generic derived types.
+ The open generic type '{0}' cannot be used as a derived type for the non-generic polymorphic type '{1}'. The base type must be generic to resolve open generic derived types.
+
+
+
+ Open generic derived type is not supported on a non-generic base type.
+ Open generic derived type is not supported on a non-generic base type.
+
+ The type '{0}' includes the ref like property, field or constructor parameter '{1}'. No source code will be generated for the property, field or constructor.Typ {0} zahrnuje ref-like parametr vlastnosti, pole nebo konstruktoru {1}. Pro vlastnost, pole nebo konstruktor se nevygeneruje žádný zdrojový kód.
diff --git a/src/libraries/System.Text.Json/gen/Resources/xlf/Strings.de.xlf b/src/libraries/System.Text.Json/gen/Resources/xlf/Strings.de.xlf
index 52b5e98696508a..a12b4e8ddfa9ff 100644
--- a/src/libraries/System.Text.Json/gen/Resources/xlf/Strings.de.xlf
+++ b/src/libraries/System.Text.Json/gen/Resources/xlf/Strings.de.xlf
@@ -132,6 +132,16 @@
Der Typ enthält mehrere Elemente, die mit dem JsonExtensionDataAttribute versehen sind.
+
+ The open generic type '{0}' cannot be used as a derived type for the non-generic polymorphic type '{1}'. The base type must be generic to resolve open generic derived types.
+ The open generic type '{0}' cannot be used as a derived type for the non-generic polymorphic type '{1}'. The base type must be generic to resolve open generic derived types.
+
+
+
+ Open generic derived type is not supported on a non-generic base type.
+ Open generic derived type is not supported on a non-generic base type.
+
+ The type '{0}' includes the ref like property, field or constructor parameter '{1}'. No source code will be generated for the property, field or constructor.Der Typ „{0}“ enthält die Verweise wie Eigenschaft, Feld oder Konstruktorparameter „{1}“. Für die Eigenschaft, das Feld oder den Konstruktor wird kein Quellcode generiert.
diff --git a/src/libraries/System.Text.Json/gen/Resources/xlf/Strings.es.xlf b/src/libraries/System.Text.Json/gen/Resources/xlf/Strings.es.xlf
index 0c75870edb1d11..b89f3d83c7b697 100644
--- a/src/libraries/System.Text.Json/gen/Resources/xlf/Strings.es.xlf
+++ b/src/libraries/System.Text.Json/gen/Resources/xlf/Strings.es.xlf
@@ -132,6 +132,16 @@
El tipo tiene varios miembros anotados con JsonExtensionDataAttribute.
+
+ The open generic type '{0}' cannot be used as a derived type for the non-generic polymorphic type '{1}'. The base type must be generic to resolve open generic derived types.
+ The open generic type '{0}' cannot be used as a derived type for the non-generic polymorphic type '{1}'. The base type must be generic to resolve open generic derived types.
+
+
+
+ Open generic derived type is not supported on a non-generic base type.
+ Open generic derived type is not supported on a non-generic base type.
+
+ The type '{0}' includes the ref like property, field or constructor parameter '{1}'. No source code will be generated for the property, field or constructor.El tipo "{0}" incluye una referencia como propiedad, campo o parámetro de constructor "{1}". No se generará código fuente para la propiedad, campo o constructor.
diff --git a/src/libraries/System.Text.Json/gen/Resources/xlf/Strings.fr.xlf b/src/libraries/System.Text.Json/gen/Resources/xlf/Strings.fr.xlf
index 6ece010bcfd65a..0680c89e68fd8f 100644
--- a/src/libraries/System.Text.Json/gen/Resources/xlf/Strings.fr.xlf
+++ b/src/libraries/System.Text.Json/gen/Resources/xlf/Strings.fr.xlf
@@ -132,6 +132,16 @@
Le type comporte plusieurs membres annotés avec JsonExtensionDataAttribute.
+
+ The open generic type '{0}' cannot be used as a derived type for the non-generic polymorphic type '{1}'. The base type must be generic to resolve open generic derived types.
+ The open generic type '{0}' cannot be used as a derived type for the non-generic polymorphic type '{1}'. The base type must be generic to resolve open generic derived types.
+
+
+
+ Open generic derived type is not supported on a non-generic base type.
+ Open generic derived type is not supported on a non-generic base type.
+
+ The type '{0}' includes the ref like property, field or constructor parameter '{1}'. No source code will be generated for the property, field or constructor.Le type « {0} » inclut le ref comme propriété, champ ou paramètre de constructeur « {1} ». Aucun code source ne sera généré pour la propriété, le champ ou le constructeur.
diff --git a/src/libraries/System.Text.Json/gen/Resources/xlf/Strings.it.xlf b/src/libraries/System.Text.Json/gen/Resources/xlf/Strings.it.xlf
index a542b8017265e5..d862bde27fc305 100644
--- a/src/libraries/System.Text.Json/gen/Resources/xlf/Strings.it.xlf
+++ b/src/libraries/System.Text.Json/gen/Resources/xlf/Strings.it.xlf
@@ -132,6 +132,16 @@
Nel tipo sono presenti più membri annotati con JsonExtensionDataAttribute.
+
+ The open generic type '{0}' cannot be used as a derived type for the non-generic polymorphic type '{1}'. The base type must be generic to resolve open generic derived types.
+ The open generic type '{0}' cannot be used as a derived type for the non-generic polymorphic type '{1}'. The base type must be generic to resolve open generic derived types.
+
+
+
+ Open generic derived type is not supported on a non-generic base type.
+ Open generic derived type is not supported on a non-generic base type.
+
+ The type '{0}' includes the ref like property, field or constructor parameter '{1}'. No source code will be generated for the property, field or constructor.Il tipo '{0}' include un riferimento come la proprietà, il campo o il parametro del costruttore '{1}'. Non verrà generato codice sorgente per la proprietà, il campo o il costruttore.
diff --git a/src/libraries/System.Text.Json/gen/Resources/xlf/Strings.ja.xlf b/src/libraries/System.Text.Json/gen/Resources/xlf/Strings.ja.xlf
index 27f29423ff6a5f..f7b1085e35da91 100644
--- a/src/libraries/System.Text.Json/gen/Resources/xlf/Strings.ja.xlf
+++ b/src/libraries/System.Text.Json/gen/Resources/xlf/Strings.ja.xlf
@@ -132,6 +132,16 @@
型には、'JsonExtensionDataAttribute' に注釈が付けられた複数のメンバーが含まれます。
+
+ The open generic type '{0}' cannot be used as a derived type for the non-generic polymorphic type '{1}'. The base type must be generic to resolve open generic derived types.
+ The open generic type '{0}' cannot be used as a derived type for the non-generic polymorphic type '{1}'. The base type must be generic to resolve open generic derived types.
+
+
+
+ Open generic derived type is not supported on a non-generic base type.
+ Open generic derived type is not supported on a non-generic base type.
+
+ The type '{0}' includes the ref like property, field or constructor parameter '{1}'. No source code will be generated for the property, field or constructor.型 '{0}' には、プロパティ、フィールド、コンストラクター パラメーター '{1}' などの ref が含まれます。プロパティ、フィールド、またはコンストラクターのソース コードは生成されません。
diff --git a/src/libraries/System.Text.Json/gen/Resources/xlf/Strings.ko.xlf b/src/libraries/System.Text.Json/gen/Resources/xlf/Strings.ko.xlf
index 8832eb4d85a761..cc7bd794882ff1 100644
--- a/src/libraries/System.Text.Json/gen/Resources/xlf/Strings.ko.xlf
+++ b/src/libraries/System.Text.Json/gen/Resources/xlf/Strings.ko.xlf
@@ -132,6 +132,16 @@
형식에 JsonExtensionDataAttribute로 주석이 추가 된 멤버가 여러 개 있습니다.
+
+ The open generic type '{0}' cannot be used as a derived type for the non-generic polymorphic type '{1}'. The base type must be generic to resolve open generic derived types.
+ The open generic type '{0}' cannot be used as a derived type for the non-generic polymorphic type '{1}'. The base type must be generic to resolve open generic derived types.
+
+
+
+ Open generic derived type is not supported on a non-generic base type.
+ Open generic derived type is not supported on a non-generic base type.
+
+ The type '{0}' includes the ref like property, field or constructor parameter '{1}'. No source code will be generated for the property, field or constructor.'{0}' 형식에는 속성, 필드 또는 생성자 매개 변수 '{1}'와 같은 ref가 포함됩니다. 속성, 필드 또는 생성자에 대한 소스 코드가 생성되지 않습니다.
diff --git a/src/libraries/System.Text.Json/gen/Resources/xlf/Strings.pl.xlf b/src/libraries/System.Text.Json/gen/Resources/xlf/Strings.pl.xlf
index 9cdc8b227b3532..9ffaaa18703481 100644
--- a/src/libraries/System.Text.Json/gen/Resources/xlf/Strings.pl.xlf
+++ b/src/libraries/System.Text.Json/gen/Resources/xlf/Strings.pl.xlf
@@ -132,6 +132,16 @@
Typ ma wiele składowych opatrzonych adnotacjami za pomocą atrybutu JsonExtensionDataAttribute.
+
+ The open generic type '{0}' cannot be used as a derived type for the non-generic polymorphic type '{1}'. The base type must be generic to resolve open generic derived types.
+ The open generic type '{0}' cannot be used as a derived type for the non-generic polymorphic type '{1}'. The base type must be generic to resolve open generic derived types.
+
+
+
+ Open generic derived type is not supported on a non-generic base type.
+ Open generic derived type is not supported on a non-generic base type.
+
+ The type '{0}' includes the ref like property, field or constructor parameter '{1}'. No source code will be generated for the property, field or constructor.Typ „{0}” zawiera parametr ref, taki jak właściwość, pole lub konstruktor „{1}”. Nie zostanie wygenerowany kod źródłowy dla właściwości, pola lub konstruktora.
diff --git a/src/libraries/System.Text.Json/gen/Resources/xlf/Strings.pt-BR.xlf b/src/libraries/System.Text.Json/gen/Resources/xlf/Strings.pt-BR.xlf
index 3b74ad1d07dfde..1e855f5ce87139 100644
--- a/src/libraries/System.Text.Json/gen/Resources/xlf/Strings.pt-BR.xlf
+++ b/src/libraries/System.Text.Json/gen/Resources/xlf/Strings.pt-BR.xlf
@@ -132,6 +132,16 @@
Tipo tem vários membros anotados com JsonExtensionDataAttribute.
+
+ The open generic type '{0}' cannot be used as a derived type for the non-generic polymorphic type '{1}'. The base type must be generic to resolve open generic derived types.
+ The open generic type '{0}' cannot be used as a derived type for the non-generic polymorphic type '{1}'. The base type must be generic to resolve open generic derived types.
+
+
+
+ Open generic derived type is not supported on a non-generic base type.
+ Open generic derived type is not supported on a non-generic base type.
+
+ The type '{0}' includes the ref like property, field or constructor parameter '{1}'. No source code will be generated for the property, field or constructor.O tipo "{0}" inclui a propriedade ref like, campo ou parâmetro de construtor "{1}". Nenhum código-fonte será gerado para a propriedade, campo ou construtor.
diff --git a/src/libraries/System.Text.Json/gen/Resources/xlf/Strings.ru.xlf b/src/libraries/System.Text.Json/gen/Resources/xlf/Strings.ru.xlf
index bd9e003fac6361..97b701de79573e 100644
--- a/src/libraries/System.Text.Json/gen/Resources/xlf/Strings.ru.xlf
+++ b/src/libraries/System.Text.Json/gen/Resources/xlf/Strings.ru.xlf
@@ -132,6 +132,16 @@
Тип содержит несколько элементов, помеченных с помощью JsonExtensionDataAttribute.
+
+ The open generic type '{0}' cannot be used as a derived type for the non-generic polymorphic type '{1}'. The base type must be generic to resolve open generic derived types.
+ The open generic type '{0}' cannot be used as a derived type for the non-generic polymorphic type '{1}'. The base type must be generic to resolve open generic derived types.
+
+
+
+ Open generic derived type is not supported on a non-generic base type.
+ Open generic derived type is not supported on a non-generic base type.
+
+ The type '{0}' includes the ref like property, field or constructor parameter '{1}'. No source code will be generated for the property, field or constructor.Тип "{0}" содержит ссылку, например свойство, поле или параметр конструктора "{1}". Для свойства, поля или конструктора не будет создан исходный код.
diff --git a/src/libraries/System.Text.Json/gen/Resources/xlf/Strings.tr.xlf b/src/libraries/System.Text.Json/gen/Resources/xlf/Strings.tr.xlf
index 0757cda7d71717..332fb8fcdb8c3a 100644
--- a/src/libraries/System.Text.Json/gen/Resources/xlf/Strings.tr.xlf
+++ b/src/libraries/System.Text.Json/gen/Resources/xlf/Strings.tr.xlf
@@ -132,6 +132,16 @@
Tür, JsonExtensionDataAttribute ile açıklanan birden çok üyeye sahip.
+
+ The open generic type '{0}' cannot be used as a derived type for the non-generic polymorphic type '{1}'. The base type must be generic to resolve open generic derived types.
+ The open generic type '{0}' cannot be used as a derived type for the non-generic polymorphic type '{1}'. The base type must be generic to resolve open generic derived types.
+
+
+
+ Open generic derived type is not supported on a non-generic base type.
+ Open generic derived type is not supported on a non-generic base type.
+
+ The type '{0}' includes the ref like property, field or constructor parameter '{1}'. No source code will be generated for the property, field or constructor.'{0}' türü; özellik, alan veya oluşturucu parametresi '{1}' gibi başvuru içeriyor. Özellik, alan veya oluşturucu için kaynak kodu üretilmeyecek.
diff --git a/src/libraries/System.Text.Json/gen/Resources/xlf/Strings.zh-Hans.xlf b/src/libraries/System.Text.Json/gen/Resources/xlf/Strings.zh-Hans.xlf
index 63e0251fec99f5..e395658677e1b5 100644
--- a/src/libraries/System.Text.Json/gen/Resources/xlf/Strings.zh-Hans.xlf
+++ b/src/libraries/System.Text.Json/gen/Resources/xlf/Strings.zh-Hans.xlf
@@ -132,6 +132,16 @@
类型具有多个带有 JsonExtensionDataAttribute 注释的成员。
+
+ The open generic type '{0}' cannot be used as a derived type for the non-generic polymorphic type '{1}'. The base type must be generic to resolve open generic derived types.
+ The open generic type '{0}' cannot be used as a derived type for the non-generic polymorphic type '{1}'. The base type must be generic to resolve open generic derived types.
+
+
+
+ Open generic derived type is not supported on a non-generic base type.
+ Open generic derived type is not supported on a non-generic base type.
+
+ The type '{0}' includes the ref like property, field or constructor parameter '{1}'. No source code will be generated for the property, field or constructor.类型“{0}”包括属性、字段或构造函数参数“{1}”等引用。不会为属性、字段或构造函数生成源代码。
diff --git a/src/libraries/System.Text.Json/gen/Resources/xlf/Strings.zh-Hant.xlf b/src/libraries/System.Text.Json/gen/Resources/xlf/Strings.zh-Hant.xlf
index 95061df16ecbb5..cee7d66617083d 100644
--- a/src/libraries/System.Text.Json/gen/Resources/xlf/Strings.zh-Hant.xlf
+++ b/src/libraries/System.Text.Json/gen/Resources/xlf/Strings.zh-Hant.xlf
@@ -132,6 +132,16 @@
類型具有使用 JsonExtensionDataAttribute 標註的多個成員。
+
+ The open generic type '{0}' cannot be used as a derived type for the non-generic polymorphic type '{1}'. The base type must be generic to resolve open generic derived types.
+ The open generic type '{0}' cannot be used as a derived type for the non-generic polymorphic type '{1}'. The base type must be generic to resolve open generic derived types.
+
+
+
+ Open generic derived type is not supported on a non-generic base type.
+ Open generic derived type is not supported on a non-generic base type.
+
+ The type '{0}' includes the ref like property, field or constructor parameter '{1}'. No source code will be generated for the property, field or constructor.類型 '{0}' 包含 ref,例如屬性、欄位或建構函式參數 '{1}'。不會針對屬性、欄位或建構函式產生原始程式碼。
diff --git a/src/libraries/System.Text.Json/src/Resources/Strings.resx b/src/libraries/System.Text.Json/src/Resources/Strings.resx
index b67fc02b7a5a86..4a777657e021e4 100644
--- a/src/libraries/System.Text.Json/src/Resources/Strings.resx
+++ b/src/libraries/System.Text.Json/src/Resources/Strings.resx
@@ -663,7 +663,10 @@
Specified type '{0}' does not support polymorphism. Polymorphic types cannot be structs, sealed types, generic types or System.Object.
- Specified type '{0}' is not a supported derived type for the polymorphic type '{1}'. Derived types must be assignable to the base type, must not be generic and cannot be abstract classes or interfaces unless 'JsonUnknownDerivedTypeHandling.FallBackToNearestAncestor' is specified.
+ Specified type '{0}' is not a supported derived type for the polymorphic type '{1}'. Derived types must be assignable to the base type, must not be open generic type definitions and cannot be abstract classes or interfaces unless 'JsonUnknownDerivedTypeHandling.FallBackToNearestAncestor' is specified.
+
+
+ The open generic type '{0}' cannot be used as a derived type for the polymorphic type '{1}'. The type arguments of the open generic derived type must be resolvable from the base type's generic arguments.The polymorphic type '{0}' has already specified derived type '{1}'.
diff --git a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Metadata/PolymorphicTypeResolver.cs b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Metadata/PolymorphicTypeResolver.cs
index 075ea31a65a380..7565f47361508e 100644
--- a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Metadata/PolymorphicTypeResolver.cs
+++ b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Metadata/PolymorphicTypeResolver.cs
@@ -36,18 +36,30 @@ public PolymorphicTypeResolver(JsonSerializerOptions options, JsonPolymorphismOp
{
Debug.Assert(typeDiscriminator is null or int or string);
- if (!IsSupportedDerivedType(BaseType, derivedType) ||
- (derivedType.IsAbstract && UnknownDerivedTypeHandling != JsonUnknownDerivedTypeHandling.FallBackToNearestAncestor))
+ Type resolvedDerivedType = derivedType;
+
+ if (derivedType is not null && derivedType.IsGenericTypeDefinition)
{
- ThrowHelper.ThrowInvalidOperationException_DerivedTypeNotSupported(BaseType, derivedType);
+ if (!TryResolveOpenGenericDerivedType(derivedType, BaseType, out Type? resolved))
+ {
+ ThrowHelper.ThrowInvalidOperationException_OpenGenericDerivedTypeNotSupported(BaseType, derivedType);
+ }
+
+ resolvedDerivedType = resolved;
}
- JsonTypeInfo derivedTypeInfo = options.GetTypeInfoInternal(derivedType);
+ if (!IsSupportedDerivedType(BaseType, resolvedDerivedType) ||
+ (resolvedDerivedType.IsAbstract && UnknownDerivedTypeHandling != JsonUnknownDerivedTypeHandling.FallBackToNearestAncestor))
+ {
+ ThrowHelper.ThrowInvalidOperationException_DerivedTypeNotSupported(BaseType, resolvedDerivedType);
+ }
+
+ JsonTypeInfo derivedTypeInfo = options.GetTypeInfoInternal(resolvedDerivedType);
DerivedJsonTypeInfo derivedTypeInfoHolder = new(typeDiscriminator, derivedTypeInfo);
- if (!_typeToDiscriminatorId.TryAdd(derivedType, derivedTypeInfoHolder))
+ if (!_typeToDiscriminatorId.TryAdd(resolvedDerivedType, derivedTypeInfoHolder))
{
- ThrowHelper.ThrowInvalidOperationException_DerivedTypeIsAlreadySpecified(BaseType, derivedType);
+ ThrowHelper.ThrowInvalidOperationException_DerivedTypeIsAlreadySpecified(BaseType, resolvedDerivedType);
}
if (typeDiscriminator is not null)
@@ -193,6 +205,153 @@ public static bool IsSupportedPolymorphicBaseType(Type? type) =>
public static bool IsSupportedDerivedType(Type baseType, Type? derivedType) =>
baseType.IsAssignableFrom(derivedType) && !derivedType.IsGenericTypeDefinition;
+ ///
+ /// Attempts to resolve an open generic derived type to a closed generic type
+ /// using the type arguments of the constructed base type.
+ ///
+ ///
+ /// For example, given Base<int> and Derived<> where
+ /// Derived<T> : Base<T>, this resolves to Derived<int>.
+ ///
+ [UnconditionalSuppressMessage("ReflectionAnalysis", "IL2055:UnrecognizedReflectionPattern",
+ Justification = "The types being constructed are derived types explicitly declared as polymorphic by the user.")]
+ [UnconditionalSuppressMessage("ReflectionAnalysis", "IL2070:UnrecognizedReflectionPattern",
+ Justification = "The call to GetInterfaces is used to find the inheritance relationship between the derived type and the base type definition.")]
+ [UnconditionalSuppressMessage("AotAnalysis", "IL3050:RequiresDynamicCode",
+ Justification = "The open generic derived types are explicitly declared by the user and MakeGenericType is used to construct them with resolved type arguments.")]
+ internal static bool TryResolveOpenGenericDerivedType(Type openDerivedType, Type constructedBaseType, [NotNullWhen(true)] out Type? closedDerivedType)
+ {
+ Debug.Assert(openDerivedType.IsGenericTypeDefinition);
+ closedDerivedType = null;
+
+ if (!constructedBaseType.IsGenericType)
+ {
+ // Base type is not generic; cannot resolve type parameters of an open generic derived type.
+ return false;
+ }
+
+ Type baseTypeDefinition = constructedBaseType.GetGenericTypeDefinition();
+ Type[] baseTypeArgs = constructedBaseType.GetGenericArguments();
+
+ // Find the ancestor of the open derived type that matches the base type definition.
+ Type? matchingBase = FindMatchingBaseType(openDerivedType, baseTypeDefinition);
+ if (matchingBase is null)
+ {
+ return false;
+ }
+
+ Type[] matchingBaseArgs = matchingBase.GetGenericArguments();
+ Debug.Assert(matchingBaseArgs.Length == baseTypeArgs.Length);
+
+ // Build a mapping from the derived type's generic parameters to concrete types.
+ Type[] derivedTypeParams = openDerivedType.GetGenericArguments();
+ Type?[] resolvedArgs = new Type?[derivedTypeParams.Length];
+
+ for (int i = 0; i < matchingBaseArgs.Length; i++)
+ {
+ if (!TryUnifyTypes(matchingBaseArgs[i], baseTypeArgs[i], derivedTypeParams, resolvedArgs))
+ {
+ return false;
+ }
+ }
+
+ // Verify all type parameters were resolved.
+ for (int i = 0; i < resolvedArgs.Length; i++)
+ {
+ if (resolvedArgs[i] is null)
+ {
+ return false;
+ }
+ }
+
+ try
+ {
+ closedDerivedType = openDerivedType.MakeGenericType(resolvedArgs!);
+ return true;
+ }
+ catch (ArgumentException)
+ {
+ // Type constraints were violated.
+ return false;
+ }
+
+ static Type? FindMatchingBaseType(Type derivedType, Type baseTypeDefinition)
+ {
+ if (baseTypeDefinition.IsInterface)
+ {
+ foreach (Type iface in derivedType.GetInterfaces())
+ {
+ if (iface.IsGenericType && iface.GetGenericTypeDefinition() == baseTypeDefinition)
+ {
+ return iface;
+ }
+ }
+ }
+ else
+ {
+ for (Type? current = derivedType.BaseType; current is not null; current = current.BaseType)
+ {
+ if (current.IsGenericType && current.GetGenericTypeDefinition() == baseTypeDefinition)
+ {
+ return current;
+ }
+ }
+ }
+
+ return null;
+ }
+
+ static bool TryUnifyTypes(Type pattern, Type concrete, Type[] typeParams, Type?[] resolvedArgs)
+ {
+ if (pattern.IsGenericParameter)
+ {
+ int index = Array.IndexOf(typeParams, pattern);
+ if (index < 0)
+ {
+ // Not one of the derived type's type parameters.
+ return false;
+ }
+
+ if (resolvedArgs[index] is null)
+ {
+ resolvedArgs[index] = concrete;
+ return true;
+ }
+
+ return resolvedArgs[index] == concrete;
+ }
+
+ if (pattern.IsGenericType)
+ {
+ if (!concrete.IsGenericType)
+ {
+ return false;
+ }
+
+ if (pattern.GetGenericTypeDefinition() != concrete.GetGenericTypeDefinition())
+ {
+ return false;
+ }
+
+ Type[] patternArgs = pattern.GetGenericArguments();
+ Type[] concreteArgs = concrete.GetGenericArguments();
+
+ for (int i = 0; i < patternArgs.Length; i++)
+ {
+ if (!TryUnifyTypes(patternArgs[i], concreteArgs[i], typeParams, resolvedArgs))
+ {
+ return false;
+ }
+ }
+
+ return true;
+ }
+
+ // Non-generic, non-parameter type: must be an exact match.
+ return pattern == concrete;
+ }
+ }
+
[UnconditionalSuppressMessage("ReflectionAnalysis", "IL2070:UnrecognizedReflectionPattern",
Justification = "The call to GetInterfaces will cross-reference results with interface types " +
"already declared as derived types of the polymorphic base type.")]
diff --git a/src/libraries/System.Text.Json/src/System/Text/Json/ThrowHelper.Serialization.cs b/src/libraries/System.Text.Json/src/System/Text/Json/ThrowHelper.Serialization.cs
index afccfa57946fe1..33035deda8a124 100644
--- a/src/libraries/System.Text.Json/src/System/Text/Json/ThrowHelper.Serialization.cs
+++ b/src/libraries/System.Text.Json/src/System/Text/Json/ThrowHelper.Serialization.cs
@@ -949,6 +949,12 @@ public static void ThrowInvalidOperationException_DerivedTypeNotSupported(Type b
throw new InvalidOperationException(SR.Format(SR.Polymorphism_DerivedTypeIsNotSupported, derivedType, baseType));
}
+ [DoesNotReturn]
+ public static void ThrowInvalidOperationException_OpenGenericDerivedTypeNotSupported(Type baseType, Type derivedType)
+ {
+ throw new InvalidOperationException(SR.Format(SR.Polymorphism_OpenGenericDerivedTypeNotSupported, derivedType, baseType));
+ }
+
[DoesNotReturn]
public static void ThrowInvalidOperationException_DerivedTypeIsAlreadySpecified(Type baseType, Type derivedType)
{
diff --git a/src/libraries/System.Text.Json/tests/System.Text.Json.Tests/Serialization/PolymorphicTests.CustomTypeHierarchies.cs b/src/libraries/System.Text.Json/tests/System.Text.Json.Tests/Serialization/PolymorphicTests.CustomTypeHierarchies.cs
index 3951eb4adac365..07197bf5e22d94 100644
--- a/src/libraries/System.Text.Json/tests/System.Text.Json.Tests/Serialization/PolymorphicTests.CustomTypeHierarchies.cs
+++ b/src/libraries/System.Text.Json/tests/System.Text.Json.Tests/Serialization/PolymorphicTests.CustomTypeHierarchies.cs
@@ -2638,10 +2638,11 @@ public class B : PolymorphicClasWithDuplicateTypeDiscriminators { }
}
[Fact]
- public async Task PolymorphicGenericClass_ThrowsInvalidOperationException()
+ public async Task PolymorphicGenericClass_SupportsOpenGenericDerivedType()
{
PolymorphicGenericClass value = new PolymorphicGenericClass.DerivedClass();
- await Assert.ThrowsAsync(() => Serializer.SerializeWrapper(value));
+ string json = await Serializer.SerializeWrapper(value);
+ Assert.Equal("{}", json);
}
[JsonDerivedType(typeof(PolymorphicGenericClass<>.DerivedClass))]
From c9e89e746d8a3c4f9e98a2953dae827c091ea270 Mon Sep 17 00:00:00 2001
From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com>
Date: Thu, 23 Apr 2026 12:26:16 +0000
Subject: [PATCH 03/12] Add comprehensive tests for open generic polymorphic
serialization
Agent-Logs-Url: https://github.com/dotnet/runtime/sessions/f39c123c-a576-4d8d-9da4-b02cd81b6577
Co-authored-by: eiriktsarpalis <2813363+eiriktsarpalis@users.noreply.github.com>
---
.../PolymorphicTests.CustomTypeHierarchies.cs | 239 ++++++++++++++++++
1 file changed, 239 insertions(+)
diff --git a/src/libraries/System.Text.Json/tests/System.Text.Json.Tests/Serialization/PolymorphicTests.CustomTypeHierarchies.cs b/src/libraries/System.Text.Json/tests/System.Text.Json.Tests/Serialization/PolymorphicTests.CustomTypeHierarchies.cs
index 07197bf5e22d94..9e481edc69ab93 100644
--- a/src/libraries/System.Text.Json/tests/System.Text.Json.Tests/Serialization/PolymorphicTests.CustomTypeHierarchies.cs
+++ b/src/libraries/System.Text.Json/tests/System.Text.Json.Tests/Serialization/PolymorphicTests.CustomTypeHierarchies.cs
@@ -2668,6 +2668,245 @@ public class DerivedClass : PolymorphicDerivedGenericClass
}
}
+ #region Open Generic Polymorphism Tests
+
+ [Fact]
+ public async Task OpenGenericDerivedType_WithStringDiscriminator_SerializationWorks()
+ {
+ OpenGenericBase_StringDisc value = new OpenGenericDerived_StringDisc { Value = 42 };
+ string json = await Serializer.SerializeWrapper(value);
+ JsonTestHelper.AssertJsonEqual("""{"$type":"derived","Value":42}""", json);
+ }
+
+ [Fact]
+ public async Task OpenGenericDerivedType_WithStringDiscriminator_DeserializationWorks()
+ {
+ string json = """{"$type":"derived","Value":42}""";
+ var result = await Serializer.DeserializeWrapper>(json);
+ Assert.IsType>(result);
+ Assert.Equal(42, ((OpenGenericDerived_StringDisc)result).Value);
+ }
+
+ [JsonDerivedType(typeof(OpenGenericDerived_StringDisc<>), "derived")]
+ public class OpenGenericBase_StringDisc
+ {
+ public T? Value { get; set; }
+ }
+
+ public class OpenGenericDerived_StringDisc : OpenGenericBase_StringDisc;
+
+ [Fact]
+ public async Task OpenGenericDerivedType_WithIntDiscriminator_SerializationWorks()
+ {
+ OpenGenericBase_IntDisc value = new OpenGenericDerived_IntDisc { Value = "hello" };
+ string json = await Serializer.SerializeWrapper(value);
+ JsonTestHelper.AssertJsonEqual("""{"$type":1,"Value":"hello"}""", json);
+ }
+
+ [Fact]
+ public async Task OpenGenericDerivedType_WithIntDiscriminator_DeserializationWorks()
+ {
+ string json = """{"$type":1,"Value":"hello"}""";
+ var result = await Serializer.DeserializeWrapper>(json);
+ Assert.IsType>(result);
+ Assert.Equal("hello", ((OpenGenericDerived_IntDisc)result).Value);
+ }
+
+ [JsonDerivedType(typeof(OpenGenericDerived_IntDisc<>), 1)]
+ public class OpenGenericBase_IntDisc
+ {
+ public T? Value { get; set; }
+ }
+
+ public class OpenGenericDerived_IntDisc : OpenGenericBase_IntDisc;
+
+ [Fact]
+ public async Task OpenGenericDerivedType_MultipleDerivedTypes_Work()
+ {
+ OpenGenericBase_Multi valueA = new OpenGenericDerivedA_Multi { ValueA = 1 };
+ OpenGenericBase_Multi valueB = new OpenGenericDerivedB_Multi { ValueB = 2 };
+
+ string jsonA = await Serializer.SerializeWrapper(valueA);
+ string jsonB = await Serializer.SerializeWrapper(valueB);
+
+ JsonTestHelper.AssertJsonEqual("""{"$type":"a","ValueA":1}""", jsonA);
+ JsonTestHelper.AssertJsonEqual("""{"$type":"b","ValueB":2}""", jsonB);
+
+ var resultA = await Serializer.DeserializeWrapper>(jsonA);
+ var resultB = await Serializer.DeserializeWrapper>(jsonB);
+
+ Assert.IsType>(resultA);
+ Assert.IsType>(resultB);
+ }
+
+ [JsonDerivedType(typeof(OpenGenericDerivedA_Multi<>), "a")]
+ [JsonDerivedType(typeof(OpenGenericDerivedB_Multi<>), "b")]
+ public class OpenGenericBase_Multi;
+
+ public class OpenGenericDerivedA_Multi : OpenGenericBase_Multi
+ {
+ public int ValueA { get; set; }
+ }
+
+ public class OpenGenericDerivedB_Multi : OpenGenericBase_Multi
+ {
+ public int ValueB { get; set; }
+ }
+
+ [Fact]
+ public async Task OpenGenericDerivedType_NestedClass_Works()
+ {
+ OpenGenericBase_Nested value = new OpenGenericBase_Nested.Derived();
+ string json = await Serializer.SerializeWrapper(value);
+ JsonTestHelper.AssertJsonEqual("""{"$type":"nested"}""", json);
+ }
+
+ [JsonDerivedType(typeof(OpenGenericBase_Nested<>.Derived), "nested")]
+ public class OpenGenericBase_Nested
+ {
+ public class Derived : OpenGenericBase_Nested;
+ }
+
+ [Fact]
+ public async Task OpenGenericDerivedType_ComplexTypeArg_Works()
+ {
+ OpenGenericBase_ComplexArg> value = new OpenGenericDerived_ComplexArg> { Data = [1, 2, 3] };
+ string json = await Serializer.SerializeWrapper(value);
+ JsonTestHelper.AssertJsonEqual("""{"$type":"derived","Data":[1,2,3]}""", json);
+ }
+
+ [JsonDerivedType(typeof(OpenGenericDerived_ComplexArg<>), "derived")]
+ public class OpenGenericBase_ComplexArg
+ {
+ public T? Data { get; set; }
+ }
+
+ public class OpenGenericDerived_ComplexArg : OpenGenericBase_ComplexArg;
+
+ [Fact]
+ public async Task OpenGenericDerivedType_WrappedTypeArg_Works()
+ {
+ // Derived : Base> - the type args are related but not identical
+ OpenGenericBase_Wrapped> value = new OpenGenericDerived_Wrapped { Data = ["a", "b"] };
+ string json = await Serializer.SerializeWrapper(value);
+ JsonTestHelper.AssertJsonEqual("""{"$type":"derived","Data":["a","b"]}""", json);
+
+ var result = await Serializer.DeserializeWrapper>>(json);
+ Assert.IsType>(result);
+ }
+
+ [JsonDerivedType(typeof(OpenGenericDerived_Wrapped<>), "derived")]
+ public class OpenGenericBase_Wrapped
+ {
+ public T? Data { get; set; }
+ }
+
+ public class OpenGenericDerived_Wrapped : OpenGenericBase_Wrapped>;
+
+ [Fact]
+ public async Task OpenGenericDerivedType_Interface_Works()
+ {
+ IOpenGenericBase value = new OpenGenericInterfaceImpl { Value = 42 };
+ string json = await Serializer.SerializeWrapper(value);
+ JsonTestHelper.AssertJsonEqual("""{"$type":"impl","Value":42}""", json);
+
+ var result = await Serializer.DeserializeWrapper>(json);
+ Assert.IsType>(result);
+ }
+
+ [JsonDerivedType(typeof(OpenGenericInterfaceImpl<>), "impl")]
+ public interface IOpenGenericBase
+ {
+ T? Value { get; set; }
+ }
+
+ public class OpenGenericInterfaceImpl : IOpenGenericBase
+ {
+ public T? Value { get; set; }
+ }
+
+ [Fact]
+ public async Task OpenGenericDerivedType_DifferentTypeArguments_ProduceDifferentResults()
+ {
+ OpenGenericBase_StringDisc intValue = new OpenGenericDerived_StringDisc { Value = 42 };
+ OpenGenericBase_StringDisc strValue = new OpenGenericDerived_StringDisc { Value = "hello" };
+
+ string intJson = await Serializer.SerializeWrapper(intValue);
+ string strJson = await Serializer.SerializeWrapper(strValue);
+
+ JsonTestHelper.AssertJsonEqual("""{"$type":"derived","Value":42}""", intJson);
+ JsonTestHelper.AssertJsonEqual("""{"$type":"derived","Value":"hello"}""", strJson);
+ }
+
+ [Fact]
+ public async Task OpenGenericDerivedType_NonGenericBase_ThrowsInvalidOperationException()
+ {
+ var value = new NonGenericBaseWithOpenGenericDerived();
+ await Assert.ThrowsAsync(() => Serializer.SerializeWrapper(value));
+ }
+
+ [JsonDerivedType(typeof(NonGenericBaseWithOpenGenericDerived.OpenDerived<>), "derived")]
+ public class NonGenericBaseWithOpenGenericDerived
+ {
+ public class OpenDerived : NonGenericBaseWithOpenGenericDerived;
+ }
+
+ [Fact]
+ public async Task OpenGenericDerivedType_TypeArgsNotResolvable_ThrowsInvalidOperationException()
+ {
+ // Derived : Base - T cannot be determined from Base
+ var value = new OpenGenericBase_Unresolvable();
+ await Assert.ThrowsAsync(() => Serializer.SerializeWrapper(value));
+ }
+
+ [JsonDerivedType(typeof(OpenGenericDerived_Unresolvable<>), "derived")]
+ public class OpenGenericBase_Unresolvable
+ {
+ public T? Value { get; set; }
+ }
+
+ public class OpenGenericDerived_Unresolvable : OpenGenericBase_Unresolvable;
+
+ [Fact]
+ public async Task OpenGenericDerivedType_ProgrammaticApi_Works()
+ {
+ var options = new JsonSerializerOptions
+ {
+ TypeInfoResolver = new DefaultJsonTypeInfoResolver
+ {
+ Modifiers =
+ {
+ static typeInfo =>
+ {
+ if (typeInfo.Type == typeof(OpenGenericBase_Programmatic))
+ {
+ typeInfo.PolymorphismOptions = new JsonPolymorphismOptions
+ {
+ DerivedTypes =
+ {
+ new JsonDerivedType(typeof(OpenGenericDerived_Programmatic<>), "derived"),
+ }
+ };
+ }
+ }
+ }
+ }
+ };
+
+ OpenGenericBase_Programmatic value = new OpenGenericDerived_Programmatic { Value = 99 };
+ string json = JsonSerializer.Serialize(value, options);
+ JsonTestHelper.AssertJsonEqual("""{"$type":"derived","Value":99}""", json);
+ }
+
+ public class OpenGenericBase_Programmatic
+ {
+ public T? Value { get; set; }
+ }
+
+ public class OpenGenericDerived_Programmatic : OpenGenericBase_Programmatic;
+
+ #endregion
+
[Fact]
public async Task PolymorphicClass_CustomConverter_TypeDiscriminator_Serialization_ThrowsNotSupportedException()
{
From 68aa74efde2b41bfbc4176cd46bdfe412688025d Mon Sep 17 00:00:00 2001
From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com>
Date: Thu, 23 Apr 2026 12:32:39 +0000
Subject: [PATCH 04/12] Update Polymorphism_TypeDoesNotSupportPolymorphism
error message to say "open generic type definitions" instead of "generic
types"
Agent-Logs-Url: https://github.com/dotnet/runtime/sessions/f39c123c-a576-4d8d-9da4-b02cd81b6577
Co-authored-by: eiriktsarpalis <2813363+eiriktsarpalis@users.noreply.github.com>
---
src/libraries/System.Text.Json/src/Resources/Strings.resx | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/src/libraries/System.Text.Json/src/Resources/Strings.resx b/src/libraries/System.Text.Json/src/Resources/Strings.resx
index 4a777657e021e4..f84919fa616173 100644
--- a/src/libraries/System.Text.Json/src/Resources/Strings.resx
+++ b/src/libraries/System.Text.Json/src/Resources/Strings.resx
@@ -660,7 +660,7 @@
The converter for derived type '{0}' does not support metadata writes or reads.
- Specified type '{0}' does not support polymorphism. Polymorphic types cannot be structs, sealed types, generic types or System.Object.
+ Specified type '{0}' does not support polymorphism. Polymorphic types cannot be structs, sealed types, open generic type definitions or System.Object.Specified type '{0}' is not a supported derived type for the polymorphic type '{1}'. Derived types must be assignable to the base type, must not be open generic type definitions and cannot be abstract classes or interfaces unless 'JsonUnknownDerivedTypeHandling.FallBackToNearestAncestor' is specified.
From 3c4d937298112af8fdb4c050b738858cb711ac05 Mon Sep 17 00:00:00 2001
From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com>
Date: Thu, 23 Apr 2026 17:14:32 +0000
Subject: [PATCH 05/12] Architectural rework: move MakeGenericType to
reflection-only path, add source generator compile-time resolution
- Revert PolymorphicTypeResolver to original (no open generic handling)
- Move open generic resolution to DefaultJsonTypeInfoResolver.Helpers (reflection-only)
- Add TryResolveOpenGenericDerivedType with Roslyn type unification in source generator Parser
- Source generator emits resolved derived types directly into PolymorphismOptions
- Add PolymorphicDerivedTypeSpec model record for source generator
- CreateFromAttributeDeclarations skips open generic derived types (null-safe)
- Update diagnostic description to cover broader unresolvable cases
- Fix tests: remove deserialization from no-discriminator test, use closed types in programmatic API test
Agent-Logs-Url: https://github.com/dotnet/runtime/sessions/ad0de94b-b94b-48dd-8df5-4159bc77a3c0
Co-authored-by: eiriktsarpalis <2813363+eiriktsarpalis@users.noreply.github.com>
---
docs/project/list-of-diagnostics.md | 2 +-
.../gen/JsonSourceGenerator.Emitter.cs | 16 ++
.../gen/JsonSourceGenerator.Parser.cs | 160 ++++++++++++++-
.../gen/Model/PolymorphicDerivedTypeSpec.cs | 20 ++
.../gen/Model/TypeGenerationSpec.cs | 7 +
.../gen/Resources/Strings.resx | 4 +-
.../gen/Resources/xlf/Strings.cs.xlf | 8 +-
.../gen/Resources/xlf/Strings.de.xlf | 8 +-
.../gen/Resources/xlf/Strings.es.xlf | 8 +-
.../gen/Resources/xlf/Strings.fr.xlf | 8 +-
.../gen/Resources/xlf/Strings.it.xlf | 8 +-
.../gen/Resources/xlf/Strings.ja.xlf | 8 +-
.../gen/Resources/xlf/Strings.ko.xlf | 8 +-
.../gen/Resources/xlf/Strings.pl.xlf | 8 +-
.../gen/Resources/xlf/Strings.pt-BR.xlf | 8 +-
.../gen/Resources/xlf/Strings.ru.xlf | 8 +-
.../gen/Resources/xlf/Strings.tr.xlf | 8 +-
.../gen/Resources/xlf/Strings.zh-Hans.xlf | 8 +-
.../gen/Resources/xlf/Strings.zh-Hant.xlf | 8 +-
.../System.Text.Json.SourceGeneration.targets | 1 +
.../DefaultJsonTypeInfoResolver.Helpers.cs | 192 ++++++++++++++++++
.../Metadata/JsonPolymorphismOptions.cs | 7 +
.../Metadata/PolymorphicTypeResolver.cs | 171 +---------------
.../PolymorphicTests.CustomTypeHierarchies.cs | 13 +-
24 files changed, 468 insertions(+), 229 deletions(-)
create mode 100644 src/libraries/System.Text.Json/gen/Model/PolymorphicDerivedTypeSpec.cs
diff --git a/docs/project/list-of-diagnostics.md b/docs/project/list-of-diagnostics.md
index 561f0710e4bcc2..6db6e267c4d8cf 100644
--- a/docs/project/list-of-diagnostics.md
+++ b/docs/project/list-of-diagnostics.md
@@ -272,7 +272,7 @@ The diagnostic id values reserved for .NET Libraries analyzer warnings are `SYSL
| __`SYSLIB1224`__ | Types annotated with JsonSerializableAttribute must be classes deriving from JsonSerializerContext. |
| __`SYSLIB1225`__ | Type includes ref like property, field or constructor parameter. |
| __`SYSLIB1226`__ | 'JsonIgnoreCondition.Always' is not valid on type-level 'JsonIgnoreAttribute' annotations. |
-| __`SYSLIB1227`__ | Open generic derived type is not supported on a non-generic base type. |
+| __`SYSLIB1227`__ | Open generic derived type is not resolvable for the polymorphic base type. |
| __`SYSLIB1228`__ | _`SYSLIB1220`-`SYSLIB1229` reserved for System.Text.Json.SourceGeneration._ |
| __`SYSLIB1229`__ | _`SYSLIB1220`-`SYSLIB1229` reserved for System.Text.Json.SourceGeneration._ |
| __`SYSLIB1230`__ | Deriving from a `GeneratedComInterface`-attributed interface defined in another assembly is not supported. |
diff --git a/src/libraries/System.Text.Json/gen/JsonSourceGenerator.Emitter.cs b/src/libraries/System.Text.Json/gen/JsonSourceGenerator.Emitter.cs
index d30851a469dc1b..df4a7845b9bafd 100644
--- a/src/libraries/System.Text.Json/gen/JsonSourceGenerator.Emitter.cs
+++ b/src/libraries/System.Text.Json/gen/JsonSourceGenerator.Emitter.cs
@@ -28,6 +28,7 @@ private sealed partial class Emitter
private const string NumberHandlingPropName = "NumberHandling";
private const string UnmappedMemberHandlingPropName = "UnmappedMemberHandling";
private const string PreferredPropertyObjectCreationHandlingPropName = "PreferredPropertyObjectCreationHandling";
+ private const string PolymorphismOptionsPropName = "PolymorphismOptions";
private const string ObjectCreatorPropName = "ObjectCreator";
private const string OptionsInstanceVariableName = "Options";
private const string JsonTypeInfoLocalVariableName = "jsonTypeInfo";
@@ -582,6 +583,21 @@ private SourceText GenerateForObject(ContextGenerationSpec contextSpec, TypeGene
}
}
+ if (typeMetadata.OpenGenericDerivedTypes is { Count: > 0 } openGenericDerivedTypes)
+ {
+ writer.WriteLine();
+ foreach (PolymorphicDerivedTypeSpec derivedTypeSpec in openGenericDerivedTypes)
+ {
+ string discriminatorArg = derivedTypeSpec.TypeDiscriminator switch
+ {
+ string s => $", \"{s}\"",
+ int i => $", {i}",
+ _ => "",
+ };
+ writer.WriteLine($"({JsonTypeInfoLocalVariableName}.{PolymorphismOptionsPropName} ??= new()).DerivedTypes.Add(new global::System.Text.Json.Serialization.Metadata.JsonDerivedType(typeof({derivedTypeSpec.DerivedType.FullyQualifiedName}){discriminatorArg}));");
+ }
+ }
+
GenerateTypeInfoFactoryFooter(writer);
if (propInitMethodName != null)
diff --git a/src/libraries/System.Text.Json/gen/JsonSourceGenerator.Parser.cs b/src/libraries/System.Text.Json/gen/JsonSourceGenerator.Parser.cs
index 0a736abe948b77..a0606e91d09e94 100644
--- a/src/libraries/System.Text.Json/gen/JsonSourceGenerator.Parser.cs
+++ b/src/libraries/System.Text.Json/gen/JsonSourceGenerator.Parser.cs
@@ -601,7 +601,8 @@ private TypeGenerationSpec ParseTypeGenerationSpec(in TypeToGenerate typeToGener
out JsonIgnoreCondition? typeIgnoreCondition,
out bool foundJsonConverterAttribute,
out TypeRef? customConverterType,
- out bool isPolymorphic);
+ out bool isPolymorphic,
+ out List? openGenericDerivedTypes);
if (type is { IsRefLikeType: true } or INamedTypeSymbol { IsUnboundGenericType: true } or IErrorTypeSymbol)
{
@@ -732,6 +733,7 @@ private TypeGenerationSpec ParseTypeGenerationSpec(in TypeToGenerate typeToGener
ClassType = classType,
PrimitiveTypeKind = primitiveTypeKind,
IsPolymorphic = isPolymorphic,
+ OpenGenericDerivedTypes = openGenericDerivedTypes?.ToImmutableEquatableArray(),
NumberHandling = numberHandling,
UnmappedMemberHandling = unmappedMemberHandling,
PreferredPropertyObjectCreationHandling = preferredPropertyObjectCreationHandling,
@@ -770,7 +772,8 @@ private void ProcessTypeCustomAttributes(
out JsonIgnoreCondition? typeIgnoreCondition,
out bool foundJsonConverterAttribute,
out TypeRef? customConverterType,
- out bool isPolymorphic)
+ out bool isPolymorphic,
+ out List? openGenericDerivedTypes)
{
numberHandling = null;
unmappedMemberHandling = null;
@@ -780,6 +783,7 @@ private void ProcessTypeCustomAttributes(
customConverterType = null;
foundJsonConverterAttribute = false;
isPolymorphic = false;
+ openGenericDerivedTypes = null;
foreach (AttributeData attributeData in typeToGenerate.Type.GetAttributes())
{
@@ -848,12 +852,26 @@ private void ProcessTypeCustomAttributes(
Debug.Assert(attributeData.ConstructorArguments.Length > 0);
var derivedType = (ITypeSymbol)attributeData.ConstructorArguments[0].Value!;
- // Open generic derived types (e.g. typeof(Derived<>)) are resolved at runtime
+ // Open generic derived types (e.g. typeof(Derived<>)) are resolved at compile time
// based on the constructed base type's type arguments.
- // Skip enqueueing them for source generation.
- if (derivedType is INamedTypeSymbol { IsUnboundGenericType: true })
+ if (derivedType is INamedTypeSymbol { IsUnboundGenericType: true } unboundDerived)
{
- if (typeToGenerate.Type is not INamedTypeSymbol { IsGenericType: true })
+ if (typeToGenerate.Type is INamedTypeSymbol { IsGenericType: true } constructedBase
+ && TryResolveOpenGenericDerivedType(unboundDerived, constructedBase, out INamedTypeSymbol? resolvedType))
+ {
+ EnqueueType(resolvedType, typeToGenerate.Mode);
+
+ object? discriminator = attributeData.ConstructorArguments.Length > 1
+ ? attributeData.ConstructorArguments[1].Value
+ : null;
+
+ (openGenericDerivedTypes ??= new()).Add(new PolymorphicDerivedTypeSpec
+ {
+ DerivedType = new TypeRef(resolvedType),
+ TypeDiscriminator = discriminator,
+ });
+ }
+ else
{
ReportDiagnostic(DiagnosticDescriptors.OpenGenericDerivedTypeNotSupported, typeToGenerate.Location, derivedType.ToDisplayString(), typeToGenerate.Type.ToDisplayString());
}
@@ -873,6 +891,136 @@ private void ProcessTypeCustomAttributes(
}
}
+ ///
+ /// Resolves an unbound generic derived type to a closed type using the
+ /// type arguments from the constructed base type at compile time.
+ ///
+ private static bool TryResolveOpenGenericDerivedType(
+ INamedTypeSymbol unboundDerived,
+ INamedTypeSymbol constructedBase,
+ [NotNullWhen(true)] out INamedTypeSymbol? resolvedType)
+ {
+ resolvedType = null;
+
+ if (!constructedBase.IsGenericType)
+ {
+ return false;
+ }
+
+ INamedTypeSymbol derivedDefinition = unboundDerived.OriginalDefinition;
+ INamedTypeSymbol baseDefinition = constructedBase.OriginalDefinition;
+
+ // Find the ancestor of the derived type definition that matches the base type definition.
+ INamedTypeSymbol? matchingBase = FindMatchingBaseType(derivedDefinition, baseDefinition);
+ if (matchingBase is null)
+ {
+ return false;
+ }
+
+ var baseTypeArgs = constructedBase.TypeArguments;
+ var matchingBaseArgs = matchingBase.TypeArguments;
+ var derivedTypeParams = derivedDefinition.TypeParameters;
+ var resolved = new ITypeSymbol?[derivedTypeParams.Length];
+
+ // Unify the type arguments to build a mapping.
+ for (int i = 0; i < matchingBaseArgs.Length; i++)
+ {
+ if (!TryUnifyTypes(matchingBaseArgs[i], baseTypeArgs[i], derivedTypeParams, resolved))
+ {
+ return false;
+ }
+ }
+
+ // Verify all type parameters were resolved.
+ for (int i = 0; i < resolved.Length; i++)
+ {
+ if (resolved[i] is null)
+ {
+ return false;
+ }
+ }
+
+ resolvedType = derivedDefinition.Construct(resolved!);
+ return true;
+
+ static INamedTypeSymbol? FindMatchingBaseType(INamedTypeSymbol derivedDef, INamedTypeSymbol baseDef)
+ {
+ if (baseDef.TypeKind == TypeKind.Interface)
+ {
+ foreach (INamedTypeSymbol iface in derivedDef.AllInterfaces)
+ {
+ if (SymbolEqualityComparer.Default.Equals(iface.OriginalDefinition, baseDef))
+ {
+ return iface;
+ }
+ }
+ }
+ else
+ {
+ for (INamedTypeSymbol? current = derivedDef.BaseType; current is not null; current = current.BaseType)
+ {
+ if (SymbolEqualityComparer.Default.Equals(current.OriginalDefinition, baseDef))
+ {
+ return current;
+ }
+ }
+ }
+
+ return null;
+ }
+
+ static bool TryUnifyTypes(
+ ITypeSymbol pattern, ITypeSymbol concrete,
+ System.Collections.Immutable.ImmutableArray typeParams,
+ ITypeSymbol?[] resolvedArgs)
+ {
+ if (pattern is ITypeParameterSymbol typeParam)
+ {
+ int index = typeParams.IndexOf(typeParam, SymbolEqualityComparer.Default);
+ if (index < 0)
+ {
+ return false;
+ }
+
+ if (resolvedArgs[index] is null)
+ {
+ resolvedArgs[index] = concrete;
+ return true;
+ }
+
+ return SymbolEqualityComparer.Default.Equals(resolvedArgs[index], concrete);
+ }
+
+ if (pattern is INamedTypeSymbol { IsGenericType: true } namedPattern)
+ {
+ if (concrete is not INamedTypeSymbol { IsGenericType: true } namedConcrete)
+ {
+ return false;
+ }
+
+ if (!SymbolEqualityComparer.Default.Equals(namedPattern.OriginalDefinition, namedConcrete.OriginalDefinition))
+ {
+ return false;
+ }
+
+ var patternArgs = namedPattern.TypeArguments;
+ var concreteArgs = namedConcrete.TypeArguments;
+
+ for (int i = 0; i < patternArgs.Length; i++)
+ {
+ if (!TryUnifyTypes(patternArgs[i], concreteArgs[i], typeParams, resolvedArgs))
+ {
+ return false;
+ }
+ }
+
+ return true;
+ }
+
+ return SymbolEqualityComparer.Default.Equals(pattern, concrete);
+ }
+ }
+
private bool TryResolveCollectionType(
ITypeSymbol type,
[NotNullWhen(true)] out ITypeSymbol? valueType,
diff --git a/src/libraries/System.Text.Json/gen/Model/PolymorphicDerivedTypeSpec.cs b/src/libraries/System.Text.Json/gen/Model/PolymorphicDerivedTypeSpec.cs
new file mode 100644
index 00000000000000..72994c5168a6eb
--- /dev/null
+++ b/src/libraries/System.Text.Json/gen/Model/PolymorphicDerivedTypeSpec.cs
@@ -0,0 +1,20 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+using SourceGenerators;
+
+namespace System.Text.Json.SourceGeneration
+{
+ ///
+ /// Models a resolved open generic derived type for polymorphic serialization.
+ ///
+ public sealed record PolymorphicDerivedTypeSpec
+ {
+ public required TypeRef DerivedType { get; init; }
+
+ ///
+ /// The type discriminator, either a or value, or null.
+ ///
+ public required object? TypeDiscriminator { get; init; }
+ }
+}
diff --git a/src/libraries/System.Text.Json/gen/Model/TypeGenerationSpec.cs b/src/libraries/System.Text.Json/gen/Model/TypeGenerationSpec.cs
index 92bdbac52066c2..47df7ac4c9fc83 100644
--- a/src/libraries/System.Text.Json/gen/Model/TypeGenerationSpec.cs
+++ b/src/libraries/System.Text.Json/gen/Model/TypeGenerationSpec.cs
@@ -51,6 +51,13 @@ public sealed record TypeGenerationSpec
public required bool IsPolymorphic { get; init; }
+ ///
+ /// Resolved open generic derived types for polymorphic serialization.
+ /// These are open generic [JsonDerivedType] declarations that have been
+ /// resolved to closed types at compile time using the base type's type arguments.
+ ///
+ public required ImmutableEquatableArray? OpenGenericDerivedTypes { get; init; }
+
public required bool IsValueTuple { get; init; }
public required JsonNumberHandling? NumberHandling { get; init; }
diff --git a/src/libraries/System.Text.Json/gen/Resources/Strings.resx b/src/libraries/System.Text.Json/gen/Resources/Strings.resx
index e06e5becec0600..781d4424e8c036 100644
--- a/src/libraries/System.Text.Json/gen/Resources/Strings.resx
+++ b/src/libraries/System.Text.Json/gen/Resources/Strings.resx
@@ -208,9 +208,9 @@
The type '{0}' has been annotated with 'JsonIgnoreAttribute' using 'JsonIgnoreCondition.Always' which is not valid on type declarations. The attribute will be ignored.
- Open generic derived type is not supported on a non-generic base type.
+ Open generic derived type is not resolvable for the polymorphic base type.
- The open generic type '{0}' cannot be used as a derived type for the non-generic polymorphic type '{1}'. The base type must be generic to resolve open generic derived types.
+ The open generic type '{0}' cannot be used as a derived type for the polymorphic type '{1}'. The type arguments of the open generic derived type must be resolvable from the base type's generic arguments.
diff --git a/src/libraries/System.Text.Json/gen/Resources/xlf/Strings.cs.xlf b/src/libraries/System.Text.Json/gen/Resources/xlf/Strings.cs.xlf
index 93d723f8ecc4ac..129908b23377cf 100644
--- a/src/libraries/System.Text.Json/gen/Resources/xlf/Strings.cs.xlf
+++ b/src/libraries/System.Text.Json/gen/Resources/xlf/Strings.cs.xlf
@@ -133,13 +133,13 @@
- The open generic type '{0}' cannot be used as a derived type for the non-generic polymorphic type '{1}'. The base type must be generic to resolve open generic derived types.
- The open generic type '{0}' cannot be used as a derived type for the non-generic polymorphic type '{1}'. The base type must be generic to resolve open generic derived types.
+ The open generic type '{0}' cannot be used as a derived type for the polymorphic type '{1}'. The type arguments of the open generic derived type must be resolvable from the base type's generic arguments.
+ The open generic type '{0}' cannot be used as a derived type for the polymorphic type '{1}'. The type arguments of the open generic derived type must be resolvable from the base type's generic arguments.
- Open generic derived type is not supported on a non-generic base type.
- Open generic derived type is not supported on a non-generic base type.
+ Open generic derived type is not resolvable for the polymorphic base type.
+ Open generic derived type is not resolvable for the polymorphic base type.
diff --git a/src/libraries/System.Text.Json/gen/Resources/xlf/Strings.de.xlf b/src/libraries/System.Text.Json/gen/Resources/xlf/Strings.de.xlf
index a12b4e8ddfa9ff..d1499a25d2a0f1 100644
--- a/src/libraries/System.Text.Json/gen/Resources/xlf/Strings.de.xlf
+++ b/src/libraries/System.Text.Json/gen/Resources/xlf/Strings.de.xlf
@@ -133,13 +133,13 @@
- The open generic type '{0}' cannot be used as a derived type for the non-generic polymorphic type '{1}'. The base type must be generic to resolve open generic derived types.
- The open generic type '{0}' cannot be used as a derived type for the non-generic polymorphic type '{1}'. The base type must be generic to resolve open generic derived types.
+ The open generic type '{0}' cannot be used as a derived type for the polymorphic type '{1}'. The type arguments of the open generic derived type must be resolvable from the base type's generic arguments.
+ The open generic type '{0}' cannot be used as a derived type for the polymorphic type '{1}'. The type arguments of the open generic derived type must be resolvable from the base type's generic arguments.
- Open generic derived type is not supported on a non-generic base type.
- Open generic derived type is not supported on a non-generic base type.
+ Open generic derived type is not resolvable for the polymorphic base type.
+ Open generic derived type is not resolvable for the polymorphic base type.
diff --git a/src/libraries/System.Text.Json/gen/Resources/xlf/Strings.es.xlf b/src/libraries/System.Text.Json/gen/Resources/xlf/Strings.es.xlf
index b89f3d83c7b697..23d1f2d3e4267b 100644
--- a/src/libraries/System.Text.Json/gen/Resources/xlf/Strings.es.xlf
+++ b/src/libraries/System.Text.Json/gen/Resources/xlf/Strings.es.xlf
@@ -133,13 +133,13 @@
- The open generic type '{0}' cannot be used as a derived type for the non-generic polymorphic type '{1}'. The base type must be generic to resolve open generic derived types.
- The open generic type '{0}' cannot be used as a derived type for the non-generic polymorphic type '{1}'. The base type must be generic to resolve open generic derived types.
+ The open generic type '{0}' cannot be used as a derived type for the polymorphic type '{1}'. The type arguments of the open generic derived type must be resolvable from the base type's generic arguments.
+ The open generic type '{0}' cannot be used as a derived type for the polymorphic type '{1}'. The type arguments of the open generic derived type must be resolvable from the base type's generic arguments.
- Open generic derived type is not supported on a non-generic base type.
- Open generic derived type is not supported on a non-generic base type.
+ Open generic derived type is not resolvable for the polymorphic base type.
+ Open generic derived type is not resolvable for the polymorphic base type.
diff --git a/src/libraries/System.Text.Json/gen/Resources/xlf/Strings.fr.xlf b/src/libraries/System.Text.Json/gen/Resources/xlf/Strings.fr.xlf
index 0680c89e68fd8f..3b88eb10c5e591 100644
--- a/src/libraries/System.Text.Json/gen/Resources/xlf/Strings.fr.xlf
+++ b/src/libraries/System.Text.Json/gen/Resources/xlf/Strings.fr.xlf
@@ -133,13 +133,13 @@
- The open generic type '{0}' cannot be used as a derived type for the non-generic polymorphic type '{1}'. The base type must be generic to resolve open generic derived types.
- The open generic type '{0}' cannot be used as a derived type for the non-generic polymorphic type '{1}'. The base type must be generic to resolve open generic derived types.
+ The open generic type '{0}' cannot be used as a derived type for the polymorphic type '{1}'. The type arguments of the open generic derived type must be resolvable from the base type's generic arguments.
+ The open generic type '{0}' cannot be used as a derived type for the polymorphic type '{1}'. The type arguments of the open generic derived type must be resolvable from the base type's generic arguments.
- Open generic derived type is not supported on a non-generic base type.
- Open generic derived type is not supported on a non-generic base type.
+ Open generic derived type is not resolvable for the polymorphic base type.
+ Open generic derived type is not resolvable for the polymorphic base type.
diff --git a/src/libraries/System.Text.Json/gen/Resources/xlf/Strings.it.xlf b/src/libraries/System.Text.Json/gen/Resources/xlf/Strings.it.xlf
index d862bde27fc305..cde61c230f5fd0 100644
--- a/src/libraries/System.Text.Json/gen/Resources/xlf/Strings.it.xlf
+++ b/src/libraries/System.Text.Json/gen/Resources/xlf/Strings.it.xlf
@@ -133,13 +133,13 @@
- The open generic type '{0}' cannot be used as a derived type for the non-generic polymorphic type '{1}'. The base type must be generic to resolve open generic derived types.
- The open generic type '{0}' cannot be used as a derived type for the non-generic polymorphic type '{1}'. The base type must be generic to resolve open generic derived types.
+ The open generic type '{0}' cannot be used as a derived type for the polymorphic type '{1}'. The type arguments of the open generic derived type must be resolvable from the base type's generic arguments.
+ The open generic type '{0}' cannot be used as a derived type for the polymorphic type '{1}'. The type arguments of the open generic derived type must be resolvable from the base type's generic arguments.
- Open generic derived type is not supported on a non-generic base type.
- Open generic derived type is not supported on a non-generic base type.
+ Open generic derived type is not resolvable for the polymorphic base type.
+ Open generic derived type is not resolvable for the polymorphic base type.
diff --git a/src/libraries/System.Text.Json/gen/Resources/xlf/Strings.ja.xlf b/src/libraries/System.Text.Json/gen/Resources/xlf/Strings.ja.xlf
index f7b1085e35da91..f3947bce538fad 100644
--- a/src/libraries/System.Text.Json/gen/Resources/xlf/Strings.ja.xlf
+++ b/src/libraries/System.Text.Json/gen/Resources/xlf/Strings.ja.xlf
@@ -133,13 +133,13 @@
- The open generic type '{0}' cannot be used as a derived type for the non-generic polymorphic type '{1}'. The base type must be generic to resolve open generic derived types.
- The open generic type '{0}' cannot be used as a derived type for the non-generic polymorphic type '{1}'. The base type must be generic to resolve open generic derived types.
+ The open generic type '{0}' cannot be used as a derived type for the polymorphic type '{1}'. The type arguments of the open generic derived type must be resolvable from the base type's generic arguments.
+ The open generic type '{0}' cannot be used as a derived type for the polymorphic type '{1}'. The type arguments of the open generic derived type must be resolvable from the base type's generic arguments.
- Open generic derived type is not supported on a non-generic base type.
- Open generic derived type is not supported on a non-generic base type.
+ Open generic derived type is not resolvable for the polymorphic base type.
+ Open generic derived type is not resolvable for the polymorphic base type.
diff --git a/src/libraries/System.Text.Json/gen/Resources/xlf/Strings.ko.xlf b/src/libraries/System.Text.Json/gen/Resources/xlf/Strings.ko.xlf
index cc7bd794882ff1..99bf103ab3bca3 100644
--- a/src/libraries/System.Text.Json/gen/Resources/xlf/Strings.ko.xlf
+++ b/src/libraries/System.Text.Json/gen/Resources/xlf/Strings.ko.xlf
@@ -133,13 +133,13 @@
- The open generic type '{0}' cannot be used as a derived type for the non-generic polymorphic type '{1}'. The base type must be generic to resolve open generic derived types.
- The open generic type '{0}' cannot be used as a derived type for the non-generic polymorphic type '{1}'. The base type must be generic to resolve open generic derived types.
+ The open generic type '{0}' cannot be used as a derived type for the polymorphic type '{1}'. The type arguments of the open generic derived type must be resolvable from the base type's generic arguments.
+ The open generic type '{0}' cannot be used as a derived type for the polymorphic type '{1}'. The type arguments of the open generic derived type must be resolvable from the base type's generic arguments.
- Open generic derived type is not supported on a non-generic base type.
- Open generic derived type is not supported on a non-generic base type.
+ Open generic derived type is not resolvable for the polymorphic base type.
+ Open generic derived type is not resolvable for the polymorphic base type.
diff --git a/src/libraries/System.Text.Json/gen/Resources/xlf/Strings.pl.xlf b/src/libraries/System.Text.Json/gen/Resources/xlf/Strings.pl.xlf
index 9ffaaa18703481..a22b9d908d42e9 100644
--- a/src/libraries/System.Text.Json/gen/Resources/xlf/Strings.pl.xlf
+++ b/src/libraries/System.Text.Json/gen/Resources/xlf/Strings.pl.xlf
@@ -133,13 +133,13 @@
- The open generic type '{0}' cannot be used as a derived type for the non-generic polymorphic type '{1}'. The base type must be generic to resolve open generic derived types.
- The open generic type '{0}' cannot be used as a derived type for the non-generic polymorphic type '{1}'. The base type must be generic to resolve open generic derived types.
+ The open generic type '{0}' cannot be used as a derived type for the polymorphic type '{1}'. The type arguments of the open generic derived type must be resolvable from the base type's generic arguments.
+ The open generic type '{0}' cannot be used as a derived type for the polymorphic type '{1}'. The type arguments of the open generic derived type must be resolvable from the base type's generic arguments.
- Open generic derived type is not supported on a non-generic base type.
- Open generic derived type is not supported on a non-generic base type.
+ Open generic derived type is not resolvable for the polymorphic base type.
+ Open generic derived type is not resolvable for the polymorphic base type.
diff --git a/src/libraries/System.Text.Json/gen/Resources/xlf/Strings.pt-BR.xlf b/src/libraries/System.Text.Json/gen/Resources/xlf/Strings.pt-BR.xlf
index 1e855f5ce87139..3f182fd646720a 100644
--- a/src/libraries/System.Text.Json/gen/Resources/xlf/Strings.pt-BR.xlf
+++ b/src/libraries/System.Text.Json/gen/Resources/xlf/Strings.pt-BR.xlf
@@ -133,13 +133,13 @@
- The open generic type '{0}' cannot be used as a derived type for the non-generic polymorphic type '{1}'. The base type must be generic to resolve open generic derived types.
- The open generic type '{0}' cannot be used as a derived type for the non-generic polymorphic type '{1}'. The base type must be generic to resolve open generic derived types.
+ The open generic type '{0}' cannot be used as a derived type for the polymorphic type '{1}'. The type arguments of the open generic derived type must be resolvable from the base type's generic arguments.
+ The open generic type '{0}' cannot be used as a derived type for the polymorphic type '{1}'. The type arguments of the open generic derived type must be resolvable from the base type's generic arguments.
- Open generic derived type is not supported on a non-generic base type.
- Open generic derived type is not supported on a non-generic base type.
+ Open generic derived type is not resolvable for the polymorphic base type.
+ Open generic derived type is not resolvable for the polymorphic base type.
diff --git a/src/libraries/System.Text.Json/gen/Resources/xlf/Strings.ru.xlf b/src/libraries/System.Text.Json/gen/Resources/xlf/Strings.ru.xlf
index 97b701de79573e..20012f312f45b3 100644
--- a/src/libraries/System.Text.Json/gen/Resources/xlf/Strings.ru.xlf
+++ b/src/libraries/System.Text.Json/gen/Resources/xlf/Strings.ru.xlf
@@ -133,13 +133,13 @@
- The open generic type '{0}' cannot be used as a derived type for the non-generic polymorphic type '{1}'. The base type must be generic to resolve open generic derived types.
- The open generic type '{0}' cannot be used as a derived type for the non-generic polymorphic type '{1}'. The base type must be generic to resolve open generic derived types.
+ The open generic type '{0}' cannot be used as a derived type for the polymorphic type '{1}'. The type arguments of the open generic derived type must be resolvable from the base type's generic arguments.
+ The open generic type '{0}' cannot be used as a derived type for the polymorphic type '{1}'. The type arguments of the open generic derived type must be resolvable from the base type's generic arguments.
- Open generic derived type is not supported on a non-generic base type.
- Open generic derived type is not supported on a non-generic base type.
+ Open generic derived type is not resolvable for the polymorphic base type.
+ Open generic derived type is not resolvable for the polymorphic base type.
diff --git a/src/libraries/System.Text.Json/gen/Resources/xlf/Strings.tr.xlf b/src/libraries/System.Text.Json/gen/Resources/xlf/Strings.tr.xlf
index 332fb8fcdb8c3a..3d24d32066d0d0 100644
--- a/src/libraries/System.Text.Json/gen/Resources/xlf/Strings.tr.xlf
+++ b/src/libraries/System.Text.Json/gen/Resources/xlf/Strings.tr.xlf
@@ -133,13 +133,13 @@
- The open generic type '{0}' cannot be used as a derived type for the non-generic polymorphic type '{1}'. The base type must be generic to resolve open generic derived types.
- The open generic type '{0}' cannot be used as a derived type for the non-generic polymorphic type '{1}'. The base type must be generic to resolve open generic derived types.
+ The open generic type '{0}' cannot be used as a derived type for the polymorphic type '{1}'. The type arguments of the open generic derived type must be resolvable from the base type's generic arguments.
+ The open generic type '{0}' cannot be used as a derived type for the polymorphic type '{1}'. The type arguments of the open generic derived type must be resolvable from the base type's generic arguments.
- Open generic derived type is not supported on a non-generic base type.
- Open generic derived type is not supported on a non-generic base type.
+ Open generic derived type is not resolvable for the polymorphic base type.
+ Open generic derived type is not resolvable for the polymorphic base type.
diff --git a/src/libraries/System.Text.Json/gen/Resources/xlf/Strings.zh-Hans.xlf b/src/libraries/System.Text.Json/gen/Resources/xlf/Strings.zh-Hans.xlf
index e395658677e1b5..1607a8da9c63fa 100644
--- a/src/libraries/System.Text.Json/gen/Resources/xlf/Strings.zh-Hans.xlf
+++ b/src/libraries/System.Text.Json/gen/Resources/xlf/Strings.zh-Hans.xlf
@@ -133,13 +133,13 @@
- The open generic type '{0}' cannot be used as a derived type for the non-generic polymorphic type '{1}'. The base type must be generic to resolve open generic derived types.
- The open generic type '{0}' cannot be used as a derived type for the non-generic polymorphic type '{1}'. The base type must be generic to resolve open generic derived types.
+ The open generic type '{0}' cannot be used as a derived type for the polymorphic type '{1}'. The type arguments of the open generic derived type must be resolvable from the base type's generic arguments.
+ The open generic type '{0}' cannot be used as a derived type for the polymorphic type '{1}'. The type arguments of the open generic derived type must be resolvable from the base type's generic arguments.
- Open generic derived type is not supported on a non-generic base type.
- Open generic derived type is not supported on a non-generic base type.
+ Open generic derived type is not resolvable for the polymorphic base type.
+ Open generic derived type is not resolvable for the polymorphic base type.
diff --git a/src/libraries/System.Text.Json/gen/Resources/xlf/Strings.zh-Hant.xlf b/src/libraries/System.Text.Json/gen/Resources/xlf/Strings.zh-Hant.xlf
index cee7d66617083d..c65fcbcc6ee622 100644
--- a/src/libraries/System.Text.Json/gen/Resources/xlf/Strings.zh-Hant.xlf
+++ b/src/libraries/System.Text.Json/gen/Resources/xlf/Strings.zh-Hant.xlf
@@ -133,13 +133,13 @@
- The open generic type '{0}' cannot be used as a derived type for the non-generic polymorphic type '{1}'. The base type must be generic to resolve open generic derived types.
- The open generic type '{0}' cannot be used as a derived type for the non-generic polymorphic type '{1}'. The base type must be generic to resolve open generic derived types.
+ The open generic type '{0}' cannot be used as a derived type for the polymorphic type '{1}'. The type arguments of the open generic derived type must be resolvable from the base type's generic arguments.
+ The open generic type '{0}' cannot be used as a derived type for the polymorphic type '{1}'. The type arguments of the open generic derived type must be resolvable from the base type's generic arguments.
- Open generic derived type is not supported on a non-generic base type.
- Open generic derived type is not supported on a non-generic base type.
+ Open generic derived type is not resolvable for the polymorphic base type.
+ Open generic derived type is not resolvable for the polymorphic base type.
diff --git a/src/libraries/System.Text.Json/gen/System.Text.Json.SourceGeneration.targets b/src/libraries/System.Text.Json/gen/System.Text.Json.SourceGeneration.targets
index 0c7c4cead803df..abdaa73bfb0823 100644
--- a/src/libraries/System.Text.Json/gen/System.Text.Json.SourceGeneration.targets
+++ b/src/libraries/System.Text.Json/gen/System.Text.Json.SourceGeneration.targets
@@ -73,6 +73,7 @@
+
diff --git a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Metadata/DefaultJsonTypeInfoResolver.Helpers.cs b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Metadata/DefaultJsonTypeInfoResolver.Helpers.cs
index 697bdc2b7b7300..b16cda2abc6207 100644
--- a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Metadata/DefaultJsonTypeInfoResolver.Helpers.cs
+++ b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Metadata/DefaultJsonTypeInfoResolver.Helpers.cs
@@ -64,6 +64,7 @@ private static JsonTypeInfo CreateTypeInfoCore(Type type, JsonConverter converte
}
typeInfo.PopulatePolymorphismMetadata();
+ ResolveOpenGenericDerivedTypes(typeInfo);
typeInfo.MapInterfaceTypesToCallbacks();
Func