From c934df0017f16770be41c1e397bc49629bcb7337 Mon Sep 17 00:00:00 2001
From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com>
Date: Sat, 11 Oct 2025 17:42:44 +0000
Subject: [PATCH 1/6] Initial plan
From 01aa977ccef7d1feb0773cd86b7a6fb839813bd6 Mon Sep 17 00:00:00 2001
From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com>
Date: Sat, 11 Oct 2025 18:29:03 +0000
Subject: [PATCH 2/6] Add IReadOnlyDictionary support for JsonExtensionData
attribute
Co-authored-by: stephentoub <2642209+stephentoub@users.noreply.github.com>
---
.../Attributes/JsonExtensionDataAttribute.cs | 8 ++-
.../JsonSerializer.Read.HandlePropertyName.cs | 20 +++++-
.../Serialization/Metadata/JsonTypeInfo.cs | 5 ++
.../tests/Common/ExtensionDataTests.cs | 69 +++++++++++++++++++
...nTypeInfoResolverTests.JsonPropertyInfo.cs | 2 +
5 files changed, 100 insertions(+), 4 deletions(-)
diff --git a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Attributes/JsonExtensionDataAttribute.cs b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Attributes/JsonExtensionDataAttribute.cs
index 42b9957d3e32de..21da84f2976293 100644
--- a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Attributes/JsonExtensionDataAttribute.cs
+++ b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Attributes/JsonExtensionDataAttribute.cs
@@ -4,12 +4,14 @@
namespace System.Text.Json.Serialization
{
///
- /// When placed on a property or field of type or
- /// , any properties that do not have a
+ /// When placed on a property or field of type ,
+ /// , or
+ /// , any properties that do not have a
/// matching property or field are added during deserialization and written during serialization.
///
///
- /// When using , the TKey value must be
+ /// When using or
+ /// , the TKey value must be
/// and TValue must be or .
///
/// During deserializing with a extension property with TValue as
diff --git a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/JsonSerializer.Read.HandlePropertyName.cs b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/JsonSerializer.Read.HandlePropertyName.cs
index 64a9de11e893a7..d018aef43c8e16 100644
--- a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/JsonSerializer.Read.HandlePropertyName.cs
+++ b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/JsonSerializer.Read.HandlePropertyName.cs
@@ -114,7 +114,9 @@ internal static void CreateExtensionDataProperty(
{
// Create the appropriate dictionary type. We already verified the types.
#if DEBUG
- Type underlyingIDictionaryType = jsonPropertyInfo.PropertyType.GetCompatibleGenericInterface(typeof(IDictionary<,>))!;
+ Type? underlyingIDictionaryType = jsonPropertyInfo.PropertyType.GetCompatibleGenericInterface(typeof(IDictionary<,>))
+ ?? jsonPropertyInfo.PropertyType.GetCompatibleGenericInterface(typeof(IReadOnlyDictionary<,>));
+ Debug.Assert(underlyingIDictionaryType is not null);
Type[] genericArgs = underlyingIDictionaryType.GetGenericArguments();
Debug.Assert(underlyingIDictionaryType.IsGenericType);
@@ -136,6 +138,22 @@ internal static void CreateExtensionDataProperty(
{
ThrowHelper.ThrowInvalidOperationException_NodeJsonObjectCustomConverterNotAllowedOnExtensionProperty();
}
+ // For IReadOnlyDictionary or IReadOnlyDictionary,
+ // create a Dictionary instance
+ else if (typeof(IReadOnlyDictionary).IsAssignableFrom(jsonPropertyInfo.PropertyType))
+ {
+ extensionData = new Dictionary();
+ Debug.Assert(jsonPropertyInfo.Set != null);
+ jsonPropertyInfo.Set(obj, extensionData);
+ return;
+ }
+ else if (typeof(IReadOnlyDictionary).IsAssignableFrom(jsonPropertyInfo.PropertyType))
+ {
+ extensionData = new Dictionary();
+ Debug.Assert(jsonPropertyInfo.Set != null);
+ jsonPropertyInfo.Set(obj, extensionData);
+ return;
+ }
else
{
ThrowHelper.ThrowNotSupportedException_SerializationNotSupported(jsonPropertyInfo.PropertyType);
diff --git a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Metadata/JsonTypeInfo.cs b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Metadata/JsonTypeInfo.cs
index 10e6db00dca22b..3e0e40b5e823ab 100644
--- a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Metadata/JsonTypeInfo.cs
+++ b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Metadata/JsonTypeInfo.cs
@@ -1326,6 +1326,11 @@ internal static bool IsValidExtensionDataProperty(Type propertyType)
{
return typeof(IDictionary).IsAssignableFrom(propertyType) ||
typeof(IDictionary).IsAssignableFrom(propertyType) ||
+ // IReadOnlyDictionary is supported only if a Dictionary can be assigned to it (e.g., the interface itself)
+ (typeof(IReadOnlyDictionary).IsAssignableFrom(propertyType) &&
+ propertyType.IsAssignableFrom(typeof(Dictionary))) ||
+ (typeof(IReadOnlyDictionary).IsAssignableFrom(propertyType) &&
+ propertyType.IsAssignableFrom(typeof(Dictionary))) ||
// Avoid a reference to typeof(JsonNode) to support trimming.
(propertyType.FullName == JsonObjectTypeName && ReferenceEquals(propertyType.Assembly, typeof(JsonTypeInfo).Assembly));
}
diff --git a/src/libraries/System.Text.Json/tests/Common/ExtensionDataTests.cs b/src/libraries/System.Text.Json/tests/Common/ExtensionDataTests.cs
index 5a675f2851df04..ea28be6938735b 100644
--- a/src/libraries/System.Text.Json/tests/Common/ExtensionDataTests.cs
+++ b/src/libraries/System.Text.Json/tests/Common/ExtensionDataTests.cs
@@ -1483,5 +1483,74 @@ public class ClassWithEmptyPropertyNameAndExtensionProperty
[JsonExtensionData]
public IDictionary MyOverflow { get; set; }
}
+
+ [Fact]
+ public async Task IReadOnlyDictionary_ObjectExtensionPropertyRoundTrip()
+ {
+ string json = @"{""MyIntMissing"":2, ""MyInt"":1}";
+ ClassWithIReadOnlyDictionaryExtensionPropertyAsObjectWithProperty obj = await Serializer.DeserializeWrapper(json);
+
+ Assert.NotNull(obj.MyOverflow);
+ Assert.Equal(1, obj.MyInt);
+ Assert.IsType(obj.MyOverflow["MyIntMissing"]);
+ Assert.Equal(2, ((JsonElement)obj.MyOverflow["MyIntMissing"]).GetInt32());
+
+ string jsonSerialized = await Serializer.SerializeWrapper(obj);
+ Assert.Contains("\"MyIntMissing\"", jsonSerialized);
+ Assert.Contains("\"MyInt\"", jsonSerialized);
+ Assert.DoesNotContain(nameof(ClassWithIReadOnlyDictionaryExtensionPropertyAsObjectWithProperty.MyOverflow), jsonSerialized);
+ }
+
+ [Fact]
+ public async Task IReadOnlyDictionary_JsonElementExtensionPropertyRoundTrip()
+ {
+ string json = @"{""MyIntMissing"":2, ""MyInt"":1}";
+ ClassWithIReadOnlyDictionaryExtensionPropertyAsJsonElementWithProperty obj = await Serializer.DeserializeWrapper(json);
+
+ Assert.NotNull(obj.MyOverflow);
+ Assert.Equal(1, obj.MyInt);
+ Assert.Equal(2, obj.MyOverflow["MyIntMissing"].GetInt32());
+
+ string jsonSerialized = await Serializer.SerializeWrapper(obj);
+ Assert.Contains("\"MyIntMissing\"", jsonSerialized);
+ Assert.Contains("\"MyInt\"", jsonSerialized);
+ Assert.DoesNotContain(nameof(ClassWithIReadOnlyDictionaryExtensionPropertyAsJsonElementWithProperty.MyOverflow), jsonSerialized);
+ }
+
+ [Fact]
+ public async Task IReadOnlyDictionary_ExtensionPropertyIgnoredWhenWritingDefault()
+ {
+ string expected = @"{}";
+ string actual = await Serializer.SerializeWrapper(new ClassWithIReadOnlyDictionaryExtensionPropertyAsObject());
+ Assert.Equal(expected, actual);
+ }
+
+ public class ClassWithIReadOnlyDictionaryExtensionPropertyAsObject
+ {
+ [JsonExtensionData]
+ public IReadOnlyDictionary MyOverflow { get; set; }
+ }
+
+ public class ClassWithIReadOnlyDictionaryExtensionPropertyAsJsonElement
+ {
+ [JsonExtensionData]
+ public IReadOnlyDictionary MyOverflow { get; set; }
+ }
+
+ public class ClassWithIReadOnlyDictionaryExtensionPropertyAsObjectWithProperty
+ {
+ public int MyInt { get; set; }
+
+ [JsonExtensionData]
+ public IReadOnlyDictionary MyOverflow { get; set; }
+ }
+
+ public class ClassWithIReadOnlyDictionaryExtensionPropertyAsJsonElementWithProperty
+ {
+ public int MyInt { get; set; }
+
+ [JsonExtensionData]
+ public IReadOnlyDictionary MyOverflow { get; set; }
+ }
}
}
diff --git a/src/libraries/System.Text.Json/tests/System.Text.Json.Tests/Serialization/MetadataTests/DefaultJsonTypeInfoResolverTests.JsonPropertyInfo.cs b/src/libraries/System.Text.Json/tests/System.Text.Json.Tests/Serialization/MetadataTests/DefaultJsonTypeInfoResolverTests.JsonPropertyInfo.cs
index 4f263a7c882895..f3ac893e9e5c19 100644
--- a/src/libraries/System.Text.Json/tests/System.Text.Json.Tests/Serialization/MetadataTests/DefaultJsonTypeInfoResolverTests.JsonPropertyInfo.cs
+++ b/src/libraries/System.Text.Json/tests/System.Text.Json.Tests/Serialization/MetadataTests/DefaultJsonTypeInfoResolverTests.JsonPropertyInfo.cs
@@ -1285,6 +1285,8 @@ public static void ClassWithExtensionDataAttribute_RemovingExtensionDataProperty
[Theory]
[InlineData(typeof(IDictionary))]
[InlineData(typeof(IDictionary))]
+ [InlineData(typeof(IReadOnlyDictionary))]
+ [InlineData(typeof(IReadOnlyDictionary))]
[InlineData(typeof(Dictionary))]
[InlineData(typeof(Dictionary))]
[InlineData(typeof(ConcurrentDictionary))]
From 9a2287e43e9bde5599c313e04fded4b1ea1f4087 Mon Sep 17 00:00:00 2001
From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com>
Date: Sat, 11 Oct 2025 18:34:20 +0000
Subject: [PATCH 3/6] Fix IReadOnlyDictionary validation to prevent
ImmutableDictionary regression
Co-authored-by: stephentoub <2642209+stephentoub@users.noreply.github.com>
---
.../JsonSerializer.Read.HandlePropertyName.cs | 11 +++++++----
1 file changed, 7 insertions(+), 4 deletions(-)
diff --git a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/JsonSerializer.Read.HandlePropertyName.cs b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/JsonSerializer.Read.HandlePropertyName.cs
index d018aef43c8e16..f442f58d5baba0 100644
--- a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/JsonSerializer.Read.HandlePropertyName.cs
+++ b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/JsonSerializer.Read.HandlePropertyName.cs
@@ -138,16 +138,19 @@ internal static void CreateExtensionDataProperty(
{
ThrowHelper.ThrowInvalidOperationException_NodeJsonObjectCustomConverterNotAllowedOnExtensionProperty();
}
- // For IReadOnlyDictionary or IReadOnlyDictionary,
- // create a Dictionary instance
- else if (typeof(IReadOnlyDictionary).IsAssignableFrom(jsonPropertyInfo.PropertyType))
+ // For IReadOnlyDictionary or IReadOnlyDictionary interface types,
+ // create a Dictionary instance. We only do this if Dictionary can be assigned back
+ // to the property (i.e., the property is the interface type itself, not a concrete implementation).
+ else if (typeof(IReadOnlyDictionary).IsAssignableFrom(jsonPropertyInfo.PropertyType) &&
+ jsonPropertyInfo.PropertyType.IsAssignableFrom(typeof(Dictionary)))
{
extensionData = new Dictionary();
Debug.Assert(jsonPropertyInfo.Set != null);
jsonPropertyInfo.Set(obj, extensionData);
return;
}
- else if (typeof(IReadOnlyDictionary).IsAssignableFrom(jsonPropertyInfo.PropertyType))
+ else if (typeof(IReadOnlyDictionary).IsAssignableFrom(jsonPropertyInfo.PropertyType) &&
+ jsonPropertyInfo.PropertyType.IsAssignableFrom(typeof(Dictionary)))
{
extensionData = new Dictionary();
Debug.Assert(jsonPropertyInfo.Set != null);
From c0c8cf6c256ca85762113b14f78b36226e12560b Mon Sep 17 00:00:00 2001
From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com>
Date: Sun, 12 Oct 2025 02:00:53 +0000
Subject: [PATCH 4/6] Fix source generator validation to support
IReadOnlyDictionary extension data
Co-authored-by: stephentoub <2642209+stephentoub@users.noreply.github.com>
---
.../gen/Helpers/KnownTypeSymbols.cs | 10 ++++++
.../gen/JsonSourceGenerator.Parser.cs | 35 ++++++++++++++++---
.../Serialization/ExtensionDataTests.cs | 8 +++++
3 files changed, 48 insertions(+), 5 deletions(-)
diff --git a/src/libraries/System.Text.Json/gen/Helpers/KnownTypeSymbols.cs b/src/libraries/System.Text.Json/gen/Helpers/KnownTypeSymbols.cs
index 0837c6f42f8871..395b2e598e3654 100644
--- a/src/libraries/System.Text.Json/gen/Helpers/KnownTypeSymbols.cs
+++ b/src/libraries/System.Text.Json/gen/Helpers/KnownTypeSymbols.cs
@@ -191,6 +191,16 @@ public KnownTypeSymbols(Compilation compilation)
public INamedTypeSymbol? JsonElementType => GetOrResolveType("System.Text.Json.JsonElement", ref _JsonElementType);
private Option _JsonElementType;
+ public INamedTypeSymbol? StringObjectDictionaryType => _StringObjectDictionaryType.HasValue
+ ? _StringObjectDictionaryType.Value
+ : (_StringObjectDictionaryType = new(DictionaryOfTKeyTValueType?.Construct(StringType, ObjectType))).Value;
+ private Option _StringObjectDictionaryType;
+
+ public INamedTypeSymbol? StringJsonElementDictionaryType => _StringJsonElementDictionaryType.HasValue
+ ? _StringJsonElementDictionaryType.Value
+ : (_StringJsonElementDictionaryType = new(DictionaryOfTKeyTValueType?.Construct(StringType, JsonElementType))).Value;
+ private Option _StringJsonElementDictionaryType;
+
public INamedTypeSymbol? JsonNodeType => GetOrResolveType("System.Text.Json.Nodes.JsonNode", ref _JsonNodeType);
private Option _JsonNodeType;
diff --git a/src/libraries/System.Text.Json/gen/JsonSourceGenerator.Parser.cs b/src/libraries/System.Text.Json/gen/JsonSourceGenerator.Parser.cs
index fc64ef50b766f3..9d4d1eacd6d2db 100644
--- a/src/libraries/System.Text.Json/gen/JsonSourceGenerator.Parser.cs
+++ b/src/libraries/System.Text.Json/gen/JsonSourceGenerator.Parser.cs
@@ -1105,14 +1105,39 @@ private bool IsValidDataExtensionPropertyType(ITypeSymbol type)
}
INamedTypeSymbol? actualDictionaryType = type.GetCompatibleGenericBaseType(_knownSymbols.IDictionaryOfTKeyTValueType);
- if (actualDictionaryType == null)
+ if (actualDictionaryType != null)
{
- return false;
+ if (SymbolEqualityComparer.Default.Equals(actualDictionaryType.TypeArguments[0], _knownSymbols.StringType) &&
+ (SymbolEqualityComparer.Default.Equals(actualDictionaryType.TypeArguments[1], _knownSymbols.ObjectType) ||
+ SymbolEqualityComparer.Default.Equals(actualDictionaryType.TypeArguments[1], _knownSymbols.JsonElementType)))
+ {
+ return true;
+ }
}
- return SymbolEqualityComparer.Default.Equals(actualDictionaryType.TypeArguments[0], _knownSymbols.StringType) &&
- (SymbolEqualityComparer.Default.Equals(actualDictionaryType.TypeArguments[1], _knownSymbols.ObjectType) ||
- SymbolEqualityComparer.Default.Equals(actualDictionaryType.TypeArguments[1], _knownSymbols.JsonElementType));
+ // Also check for IReadOnlyDictionary or IReadOnlyDictionary
+ // but only if Dictionary can be assigned to it (to exclude ImmutableDictionary and similar types)
+ INamedTypeSymbol? actualReadOnlyDictionaryType = type.GetCompatibleGenericBaseType(_knownSymbols.IReadonlyDictionaryOfTKeyTValueType);
+ if (actualReadOnlyDictionaryType != null)
+ {
+ if (SymbolEqualityComparer.Default.Equals(actualReadOnlyDictionaryType.TypeArguments[0], _knownSymbols.StringType) &&
+ (SymbolEqualityComparer.Default.Equals(actualReadOnlyDictionaryType.TypeArguments[1], _knownSymbols.ObjectType) ||
+ SymbolEqualityComparer.Default.Equals(actualReadOnlyDictionaryType.TypeArguments[1], _knownSymbols.JsonElementType)))
+ {
+ // Check if Dictionary can be assigned to this type
+ INamedTypeSymbol? dictionaryType = SymbolEqualityComparer.Default.Equals(actualReadOnlyDictionaryType.TypeArguments[1], _knownSymbols.ObjectType)
+ ? _knownSymbols.StringObjectDictionaryType
+ : _knownSymbols.StringJsonElementDictionaryType;
+
+ if (dictionaryType != null)
+ {
+ Conversion conversion = _knownSymbols.Compilation.ClassifyConversion(dictionaryType, type);
+ return conversion.IsImplicit || conversion.IsIdentity;
+ }
+ }
+ }
+
+ return false;
}
private PropertyGenerationSpec? ParsePropertyGenerationSpec(
diff --git a/src/libraries/System.Text.Json/tests/System.Text.Json.SourceGeneration.Tests/Serialization/ExtensionDataTests.cs b/src/libraries/System.Text.Json/tests/System.Text.Json.SourceGeneration.Tests/Serialization/ExtensionDataTests.cs
index ba67748368ff3b..a7d4e221e63367 100644
--- a/src/libraries/System.Text.Json/tests/System.Text.Json.SourceGeneration.Tests/Serialization/ExtensionDataTests.cs
+++ b/src/libraries/System.Text.Json/tests/System.Text.Json.SourceGeneration.Tests/Serialization/ExtensionDataTests.cs
@@ -68,6 +68,10 @@ public ExtensionDataTests_Metadata()
[JsonSerializable(typeof(int))]
[JsonSerializable(typeof(DummyObj))]
[JsonSerializable(typeof(DummyStruct))]
+ [JsonSerializable(typeof(ClassWithIReadOnlyDictionaryExtensionPropertyAsObject))]
+ [JsonSerializable(typeof(ClassWithIReadOnlyDictionaryExtensionPropertyAsJsonElement))]
+ [JsonSerializable(typeof(ClassWithIReadOnlyDictionaryExtensionPropertyAsObjectWithProperty))]
+ [JsonSerializable(typeof(ClassWithIReadOnlyDictionaryExtensionPropertyAsJsonElementWithProperty))]
internal sealed partial class ExtensionDataTestsContext_Metadata : JsonSerializerContext
{
}
@@ -132,6 +136,10 @@ public ExtensionDataTests_Default()
[JsonSerializable(typeof(int))]
[JsonSerializable(typeof(DummyObj))]
[JsonSerializable(typeof(DummyStruct))]
+ [JsonSerializable(typeof(ClassWithIReadOnlyDictionaryExtensionPropertyAsObject))]
+ [JsonSerializable(typeof(ClassWithIReadOnlyDictionaryExtensionPropertyAsJsonElement))]
+ [JsonSerializable(typeof(ClassWithIReadOnlyDictionaryExtensionPropertyAsObjectWithProperty))]
+ [JsonSerializable(typeof(ClassWithIReadOnlyDictionaryExtensionPropertyAsJsonElementWithProperty))]
internal sealed partial class ExtensionDataTestsContext_Default : JsonSerializerContext
{
}
From c98fa0d48975200e7520577eefda76ef10c33b1c Mon Sep 17 00:00:00 2001
From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com>
Date: Sun, 12 Oct 2025 12:16:43 +0000
Subject: [PATCH 5/6] Fix null reference warning in KnownTypeSymbols for
JsonElementType
Co-authored-by: stephentoub <2642209+stephentoub@users.noreply.github.com>
---
.../System.Text.Json/gen/Helpers/KnownTypeSymbols.cs | 4 +++-
1 file changed, 3 insertions(+), 1 deletion(-)
diff --git a/src/libraries/System.Text.Json/gen/Helpers/KnownTypeSymbols.cs b/src/libraries/System.Text.Json/gen/Helpers/KnownTypeSymbols.cs
index 395b2e598e3654..3827f707ee4763 100644
--- a/src/libraries/System.Text.Json/gen/Helpers/KnownTypeSymbols.cs
+++ b/src/libraries/System.Text.Json/gen/Helpers/KnownTypeSymbols.cs
@@ -198,7 +198,9 @@ public KnownTypeSymbols(Compilation compilation)
public INamedTypeSymbol? StringJsonElementDictionaryType => _StringJsonElementDictionaryType.HasValue
? _StringJsonElementDictionaryType.Value
- : (_StringJsonElementDictionaryType = new(DictionaryOfTKeyTValueType?.Construct(StringType, JsonElementType))).Value;
+ : (_StringJsonElementDictionaryType = new(DictionaryOfTKeyTValueType is { } dictType && JsonElementType is { } jsonElemType
+ ? dictType.Construct(StringType, jsonElemType)
+ : null)).Value;
private Option _StringJsonElementDictionaryType;
public INamedTypeSymbol? JsonNodeType => GetOrResolveType("System.Text.Json.Nodes.JsonNode", ref _JsonNodeType);
From 4e5e8e2966f0b199e0fcbd1d2fef035a1ef60bcc Mon Sep 17 00:00:00 2001
From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com>
Date: Mon, 13 Oct 2025 15:34:51 +0000
Subject: [PATCH 6/6] Simplify IReadOnlyDictionary validation logic
Co-authored-by: stephentoub <2642209+stephentoub@users.noreply.github.com>
---
.../Text/Json/Serialization/Metadata/JsonTypeInfo.cs | 7 ++-----
1 file changed, 2 insertions(+), 5 deletions(-)
diff --git a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Metadata/JsonTypeInfo.cs b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Metadata/JsonTypeInfo.cs
index 3e0e40b5e823ab..5456030b9bcaea 100644
--- a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Metadata/JsonTypeInfo.cs
+++ b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Metadata/JsonTypeInfo.cs
@@ -1326,11 +1326,8 @@ internal static bool IsValidExtensionDataProperty(Type propertyType)
{
return typeof(IDictionary).IsAssignableFrom(propertyType) ||
typeof(IDictionary).IsAssignableFrom(propertyType) ||
- // IReadOnlyDictionary is supported only if a Dictionary can be assigned to it (e.g., the interface itself)
- (typeof(IReadOnlyDictionary).IsAssignableFrom(propertyType) &&
- propertyType.IsAssignableFrom(typeof(Dictionary))) ||
- (typeof(IReadOnlyDictionary).IsAssignableFrom(propertyType) &&
- propertyType.IsAssignableFrom(typeof(Dictionary))) ||
+ propertyType == typeof(IReadOnlyDictionary) ||
+ propertyType == typeof(IReadOnlyDictionary) ||
// Avoid a reference to typeof(JsonNode) to support trimming.
(propertyType.FullName == JsonObjectTypeName && ReferenceEquals(propertyType.Assembly, typeof(JsonTypeInfo).Assembly));
}