Skip to content

Commit e9c9a2f

Browse files
authored
Fix InvalidCastException when deserializing some dictionary types (#42835) (#45449)
1 parent 2ee13ec commit e9c9a2f

13 files changed

Lines changed: 84 additions & 33 deletions

src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/Collection/IDictionaryConverter.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@ protected override void CreateCollection(ref Utf8JsonReader reader, ref ReadStac
3535
ThrowHelper.ThrowNotSupportedException_CannotPopulateCollection(TypeToConvert, ref reader, ref state);
3636
}
3737

38+
// Strings are intentionally used as keys when deserializing non-generic dictionaries.
3839
state.Current.ReturnValue = new Dictionary<string, object>();
3940
}
4041
else

src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/Collection/IDictionaryOfTKeyTValueConverter.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
namespace System.Text.Json.Serialization.Converters
77
{
88
/// <summary>
9-
/// Converter for <cref>System.Collections.Generic.IDictionary{string, TValue}</cref> that
9+
/// Converter for <cref>System.Collections.Generic.IDictionary{TKey, TValue}</cref> that
1010
/// (de)serializes as a JSON object with properties representing the dictionary element key and value.
1111
/// </summary>
1212
internal sealed class IDictionaryOfTKeyTValueConverter<TCollection, TKey, TValue>
@@ -30,7 +30,7 @@ protected override void CreateCollection(ref Utf8JsonReader reader, ref ReadStac
3030
ThrowHelper.ThrowNotSupportedException_CannotPopulateCollection(TypeToConvert, ref reader, ref state);
3131
}
3232

33-
state.Current.ReturnValue = new Dictionary<string, TValue>();
33+
state.Current.ReturnValue = new Dictionary<TKey, TValue>();
3434
}
3535
else
3636
{

src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/Collection/IEnumerableConverterFactoryHelpers.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -171,7 +171,7 @@ public static MethodInfo GetImmutableEnumerableCreateRangeMethod(this Type type,
171171

172172
[DynamicDependency(CreateRangeMethodNameForDictionary, ImmutableDictionaryTypeName, ImmutableCollectionsAssembly)]
173173
[DynamicDependency(CreateRangeMethodNameForDictionary, ImmutableSortedDictionaryTypeName, ImmutableCollectionsAssembly)]
174-
public static MethodInfo GetImmutableDictionaryCreateRangeMethod(this Type type, Type elementType)
174+
public static MethodInfo GetImmutableDictionaryCreateRangeMethod(this Type type, Type keyType, Type valueType)
175175
{
176176
Type? constructingType = GetImmutableDictionaryConstructingType(type);
177177
if (constructingType != null)
@@ -184,7 +184,7 @@ public static MethodInfo GetImmutableDictionaryCreateRangeMethod(this Type type,
184184
method.IsGenericMethod &&
185185
method.GetGenericArguments().Length == 2)
186186
{
187-
return method.MakeGenericMethod(typeof(string), elementType);
187+
return method.MakeGenericMethod(keyType, valueType);
188188
}
189189
}
190190
}

src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/Collection/IReadOnlyDictionaryOfTKeyTValueConverter.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ protected override void CreateCollection(ref Utf8JsonReader reader, ref ReadStac
2222
ThrowHelper.ThrowNotSupportedException_CannotPopulateCollection(TypeToConvert, ref reader, ref state);
2323
}
2424

25-
state.Current.ReturnValue = new Dictionary<string, TValue>();
25+
state.Current.ReturnValue = new Dictionary<TKey, TValue>();
2626
}
2727

2828
protected internal override bool OnWriteResume(Utf8JsonWriter writer, TCollection value, JsonSerializerOptions options, ref WriteStack state)

src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/Collection/ImmutableDictionaryOfTKeyTValueConverter.cs

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -19,21 +19,21 @@ protected override void Add(TKey key, in TValue value, JsonSerializerOptions opt
1919

2020
protected override void CreateCollection(ref Utf8JsonReader reader, ref ReadStack state)
2121
{
22-
state.Current.ReturnValue = new Dictionary<string, TValue>();
22+
state.Current.ReturnValue = new Dictionary<TKey, TValue>();
2323
}
2424

2525
protected override void ConvertCollection(ref ReadStack state, JsonSerializerOptions options)
2626
{
2727
JsonClassInfo classInfo = state.Current.JsonClassInfo;
2828

29-
Func<IEnumerable<KeyValuePair<string, TValue>>, TCollection>? creator = (Func<IEnumerable<KeyValuePair<string, TValue>>, TCollection>?)classInfo.CreateObjectWithArgs;
29+
Func<IEnumerable<KeyValuePair<TKey, TValue>>, TCollection>? creator = (Func<IEnumerable<KeyValuePair<TKey, TValue>>, TCollection>?)classInfo.CreateObjectWithArgs;
3030
if (creator == null)
3131
{
32-
creator = options.MemberAccessorStrategy.CreateImmutableDictionaryCreateRangeDelegate<TValue, TCollection>();
32+
creator = options.MemberAccessorStrategy.CreateImmutableDictionaryCreateRangeDelegate<TCollection, TKey, TValue>();
3333
classInfo.CreateObjectWithArgs = creator;
3434
}
3535

36-
state.Current.ReturnValue = creator((Dictionary<string, TValue>)state.Current.ReturnValue!);
36+
state.Current.ReturnValue = creator((Dictionary<TKey, TValue>)state.Current.ReturnValue!);
3737
}
3838

3939
protected internal override bool OnWriteResume(Utf8JsonWriter writer, TCollection value, JsonSerializerOptions options, ref WriteStack state)

src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/Collection/ImmutableEnumerableOfTConverter.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ protected override void ConvertCollection(ref ReadStack state, JsonSerializerOpt
2828
Func<IEnumerable<TElement>, TCollection>? creator = (Func<IEnumerable<TElement>, TCollection>?)classInfo.CreateObjectWithArgs;
2929
if (creator == null)
3030
{
31-
creator = options.MemberAccessorStrategy.CreateImmutableEnumerableCreateRangeDelegate<TElement, TCollection>();
31+
creator = options.MemberAccessorStrategy.CreateImmutableEnumerableCreateRangeDelegate<TCollection, TElement>();
3232
classInfo.CreateObjectWithArgs = creator;
3333
}
3434

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

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,10 @@ public override void Write(Utf8JsonWriter writer, object value, JsonSerializerOp
1919
}
2020

2121
internal override object ReadWithQuotes(ref Utf8JsonReader reader)
22-
=> throw new NotSupportedException();
22+
{
23+
ThrowHelper.ThrowNotSupportedException_DictionaryKeyTypeNotSupported(TypeToConvert);
24+
return null!;
25+
}
2326

2427
internal override void WriteWithQuotes(Utf8JsonWriter writer, object value, JsonSerializerOptions options, ref WriteStack state)
2528
{

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

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,9 +17,9 @@ public abstract JsonClassInfo.ParameterizedConstructorDelegate<T, TArg0, TArg1,
1717

1818
public abstract Action<TCollection, object?> CreateAddMethodDelegate<TCollection>();
1919

20-
public abstract Func<IEnumerable<TElement>, TCollection> CreateImmutableEnumerableCreateRangeDelegate<TElement, TCollection>();
20+
public abstract Func<IEnumerable<TElement>, TCollection> CreateImmutableEnumerableCreateRangeDelegate<TCollection, TElement>();
2121

22-
public abstract Func<IEnumerable<KeyValuePair<string, TElement>>, TCollection> CreateImmutableDictionaryCreateRangeDelegate<TElement, TCollection>();
22+
public abstract Func<IEnumerable<KeyValuePair<TKey, TValue>>, TCollection> CreateImmutableDictionaryCreateRangeDelegate<TCollection, TKey, TValue>();
2323

2424
public abstract Func<object, TProperty> CreatePropertyGetter<TProperty>(PropertyInfo propertyInfo);
2525

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

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -171,7 +171,7 @@ private static DynamicMethod CreateAddMethodDelegate(Type collectionType)
171171
return dynamicMethod;
172172
}
173173

174-
public override Func<IEnumerable<TElement>, TCollection> CreateImmutableEnumerableCreateRangeDelegate<TElement, TCollection>() =>
174+
public override Func<IEnumerable<TElement>, TCollection> CreateImmutableEnumerableCreateRangeDelegate<TCollection, TElement>() =>
175175
CreateDelegate<Func<IEnumerable<TElement>, TCollection>>(
176176
CreateImmutableEnumerableCreateRangeDelegate(typeof(TCollection), typeof(TElement), typeof(IEnumerable<TElement>)));
177177

@@ -195,13 +195,13 @@ private static DynamicMethod CreateImmutableEnumerableCreateRangeDelegate(Type c
195195
return dynamicMethod;
196196
}
197197

198-
public override Func<IEnumerable<KeyValuePair<string, TElement>>, TCollection> CreateImmutableDictionaryCreateRangeDelegate<TElement, TCollection>() =>
199-
CreateDelegate<Func<IEnumerable<KeyValuePair<string, TElement>>, TCollection>>(
200-
CreateImmutableDictionaryCreateRangeDelegate(typeof(TCollection), typeof(TElement), typeof(IEnumerable<KeyValuePair<string, TElement>>)));
198+
public override Func<IEnumerable<KeyValuePair<TKey, TValue>>, TCollection> CreateImmutableDictionaryCreateRangeDelegate<TCollection, TKey, TValue>() =>
199+
CreateDelegate<Func<IEnumerable<KeyValuePair<TKey, TValue>>, TCollection>>(
200+
CreateImmutableDictionaryCreateRangeDelegate(typeof(TCollection), typeof(TKey), typeof(TValue), typeof(IEnumerable<KeyValuePair<TKey, TValue>>)));
201201

202-
private static DynamicMethod CreateImmutableDictionaryCreateRangeDelegate(Type collectionType, Type elementType, Type enumerableType)
202+
private static DynamicMethod CreateImmutableDictionaryCreateRangeDelegate(Type collectionType, Type keyType, Type valueType, Type enumerableType)
203203
{
204-
MethodInfo realMethod = collectionType.GetImmutableDictionaryCreateRangeMethod(elementType);
204+
MethodInfo realMethod = collectionType.GetImmutableDictionaryCreateRangeMethod(keyType, valueType);
205205

206206
var dynamicMethod = new DynamicMethod(
207207
realMethod.Name,

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

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -123,18 +123,18 @@ public override JsonClassInfo.ParameterizedConstructorDelegate<T, TArg0, TArg1,
123123
};
124124
}
125125

126-
public override Func<IEnumerable<TElement>, TCollection> CreateImmutableEnumerableCreateRangeDelegate<TElement, TCollection>()
126+
public override Func<IEnumerable<TElement>, TCollection> CreateImmutableEnumerableCreateRangeDelegate<TCollection, TElement>()
127127
{
128128
MethodInfo createRange = typeof(TCollection).GetImmutableEnumerableCreateRangeMethod(typeof(TElement));
129129
return (Func<IEnumerable<TElement>, TCollection>)createRange.CreateDelegate(
130130
typeof(Func<IEnumerable<TElement>, TCollection>));
131131
}
132132

133-
public override Func<IEnumerable<KeyValuePair<string, TElement>>, TCollection> CreateImmutableDictionaryCreateRangeDelegate<TElement, TCollection>()
133+
public override Func<IEnumerable<KeyValuePair<TKey, TValue>>, TCollection> CreateImmutableDictionaryCreateRangeDelegate<TCollection, TKey, TValue>()
134134
{
135-
MethodInfo createRange = typeof(TCollection).GetImmutableDictionaryCreateRangeMethod(typeof(TElement));
136-
return (Func<IEnumerable<KeyValuePair<string, TElement>>, TCollection>)createRange.CreateDelegate(
137-
typeof(Func<IEnumerable<KeyValuePair<string, TElement>>, TCollection>));
135+
MethodInfo createRange = typeof(TCollection).GetImmutableDictionaryCreateRangeMethod(typeof(TKey), typeof(TValue));
136+
return (Func<IEnumerable<KeyValuePair<TKey, TValue>>, TCollection>)createRange.CreateDelegate(
137+
typeof(Func<IEnumerable<KeyValuePair<TKey, TValue>>, TCollection>));
138138
}
139139

140140
public override Func<object, TProperty> CreatePropertyGetter<TProperty>(PropertyInfo propertyInfo)

0 commit comments

Comments
 (0)