Skip to content

Commit 8356c8a

Browse files
Remove JsonConverter.RuntimeType (#65224)
Backports changes introduced in the polymorphic deserialization prototype. JsonConverter.RuntimeType is an implementation detail stemming from interface support in collection converters, that has leaked into the JsonTypeInfo model. Removing it makes the contract model cleaner and makes the infrastructure compatible with polymorphic deserialization.
1 parent 8871420 commit 8356c8a

37 files changed

+233
-428
lines changed

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

Lines changed: 0 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@
22
// The .NET Foundation licenses this file to you under the MIT license.
33

44
using System.Collections.Concurrent;
5-
using System.Collections.Generic;
65

76
namespace System.Text.Json.Serialization.Converters
87
{
@@ -14,15 +13,5 @@ protected override void Add(in TElement value, ref ReadStack state)
1413
{
1514
((TCollection)state.Current.ReturnValue!).Enqueue(value);
1615
}
17-
18-
protected override void CreateCollection(ref Utf8JsonReader reader, ref ReadStack state, JsonSerializerOptions options)
19-
{
20-
if (state.Current.JsonTypeInfo.CreateObject is null)
21-
{
22-
ThrowHelper.ThrowNotSupportedException_SerializationNotSupported(state.Current.JsonTypeInfo.Type);
23-
}
24-
25-
state.Current.ReturnValue = state.Current.JsonTypeInfo.CreateObject();
26-
}
2716
}
2817
}

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

Lines changed: 0 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@
22
// The .NET Foundation licenses this file to you under the MIT license.
33

44
using System.Collections.Concurrent;
5-
using System.Collections.Generic;
65

76
namespace System.Text.Json.Serialization.Converters
87
{
@@ -14,15 +13,5 @@ protected override void Add(in TElement value, ref ReadStack state)
1413
{
1514
((TCollection)state.Current.ReturnValue!).Push(value);
1615
}
17-
18-
protected override void CreateCollection(ref Utf8JsonReader reader, ref ReadStack state, JsonSerializerOptions options)
19-
{
20-
if (state.Current.JsonTypeInfo.CreateObject is null)
21-
{
22-
ThrowHelper.ThrowNotSupportedException_SerializationNotSupported(state.Current.JsonTypeInfo.Type);
23-
}
24-
25-
state.Current.ReturnValue = state.Current.JsonTypeInfo.CreateObject();
26-
}
2716
}
2817
}

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

Lines changed: 0 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -20,16 +20,6 @@ protected override void Add(TKey key, in TValue value, JsonSerializerOptions opt
2020
((TCollection)state.Current.ReturnValue!)[key] = value;
2121
}
2222

23-
protected override void CreateCollection(ref Utf8JsonReader reader, ref ReadStack state)
24-
{
25-
if (state.Current.JsonTypeInfo.CreateObject == null)
26-
{
27-
ThrowHelper.ThrowNotSupportedException_SerializationNotSupported(state.Current.JsonTypeInfo.Type);
28-
}
29-
30-
state.Current.ReturnValue = state.Current.JsonTypeInfo.CreateObject();
31-
}
32-
3323
protected internal override bool OnWriteResume(
3424
Utf8JsonWriter writer,
3525
TCollection value,

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

Lines changed: 11 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
// The .NET Foundation licenses this file to you under the MIT license.
33

44
using System.Collections.Generic;
5+
using System.Diagnostics;
56
using System.Text.Json.Serialization.Metadata;
67

78
namespace System.Text.Json.Serialization.Converters
@@ -25,45 +26,22 @@ protected override void Add(in TElement value, ref ReadStack state)
2526

2627
protected override void CreateCollection(ref Utf8JsonReader reader, ref ReadStack state, JsonSerializerOptions options)
2728
{
28-
JsonTypeInfo typeInfo = state.Current.JsonTypeInfo;
29-
30-
if (TypeToConvert.IsInterface || TypeToConvert.IsAbstract)
31-
{
32-
if (!TypeToConvert.IsAssignableFrom(RuntimeType))
33-
{
34-
ThrowHelper.ThrowNotSupportedException_CannotPopulateCollection(TypeToConvert, ref reader, ref state);
35-
}
36-
37-
state.Current.ReturnValue = new List<TElement>();
38-
}
39-
else
29+
base.CreateCollection(ref reader, ref state, options);
30+
TCollection returnValue = (TCollection)state.Current.ReturnValue!;
31+
if (returnValue.IsReadOnly)
4032
{
41-
if (typeInfo.CreateObject == null)
42-
{
43-
ThrowHelper.ThrowNotSupportedException_DeserializeNoConstructor(TypeToConvert, ref reader, ref state);
44-
}
45-
46-
TCollection returnValue = (TCollection)typeInfo.CreateObject()!;
47-
48-
if (returnValue.IsReadOnly)
49-
{
50-
ThrowHelper.ThrowNotSupportedException_CannotPopulateCollection(TypeToConvert, ref reader, ref state);
51-
}
52-
53-
state.Current.ReturnValue = returnValue;
33+
state.Current.ReturnValue = null; // clear out for more accurate JsonPath reporting.
34+
ThrowHelper.ThrowNotSupportedException_CannotPopulateCollection(TypeToConvert, ref reader, ref state);
5435
}
5536
}
5637

57-
internal override Type RuntimeType
38+
internal override void ConfigureJsonTypeInfo(JsonTypeInfo jsonTypeInfo, JsonSerializerOptions options)
5839
{
59-
get
40+
// Deserialize as List<T> for interface types that support it.
41+
if (jsonTypeInfo.CreateObject is null && TypeToConvert.IsAssignableFrom(typeof(List<TElement>)))
6042
{
61-
if (TypeToConvert.IsAbstract || TypeToConvert.IsInterface)
62-
{
63-
return typeof(List<TElement>);
64-
}
65-
66-
return TypeToConvert;
43+
Debug.Assert(TypeToConvert.IsInterface);
44+
jsonTypeInfo.CreateObject = () => new List<TElement>();
6745
}
6846
}
6947
}

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

Lines changed: 15 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33

44
using System.Collections;
55
using System.Collections.Generic;
6+
using System.Diagnostics;
67
using System.Text.Json.Serialization.Metadata;
78

89
namespace System.Text.Json.Serialization.Converters
@@ -27,33 +28,12 @@ protected override void Add(string key, in object? value, JsonSerializerOptions
2728

2829
protected override void CreateCollection(ref Utf8JsonReader reader, ref ReadStack state)
2930
{
30-
JsonTypeInfo typeInfo = state.Current.JsonTypeInfo;
31-
32-
if (TypeToConvert.IsInterface || TypeToConvert.IsAbstract)
31+
base.CreateCollection(ref reader, ref state);
32+
TDictionary returnValue = (TDictionary)state.Current.ReturnValue!;
33+
if (returnValue.IsReadOnly)
3334
{
34-
if (!TypeToConvert.IsAssignableFrom(RuntimeType))
35-
{
36-
ThrowHelper.ThrowNotSupportedException_CannotPopulateCollection(TypeToConvert, ref reader, ref state);
37-
}
38-
39-
// Strings are intentionally used as keys when deserializing non-generic dictionaries.
40-
state.Current.ReturnValue = new Dictionary<string, object>();
41-
}
42-
else
43-
{
44-
if (typeInfo.CreateObject is null)
45-
{
46-
ThrowHelper.ThrowNotSupportedException_DeserializeNoConstructor(TypeToConvert, ref reader, ref state);
47-
}
48-
49-
TDictionary returnValue = (TDictionary)typeInfo.CreateObject()!;
50-
51-
if (returnValue.IsReadOnly)
52-
{
53-
ThrowHelper.ThrowNotSupportedException_CannotPopulateCollection(TypeToConvert, ref reader, ref state);
54-
}
55-
56-
state.Current.ReturnValue = returnValue;
35+
state.Current.ReturnValue = null; // clear out for more accurate JsonPath reporting.
36+
ThrowHelper.ThrowNotSupportedException_CannotPopulateCollection(TypeToConvert, ref reader, ref state);
5737
}
5838
}
5939

@@ -115,6 +95,14 @@ protected internal override bool OnWriteResume(Utf8JsonWriter writer, TDictionar
11595
return true;
11696
}
11797

118-
internal override Type RuntimeType => TypeToConvert.IsAbstract || TypeToConvert.IsInterface ? typeof(Dictionary<string, object>) : TypeToConvert;
98+
internal override void ConfigureJsonTypeInfo(JsonTypeInfo jsonTypeInfo, JsonSerializerOptions options)
99+
{
100+
// Deserialize as Dictionary<TKey,TValue> for interface types that support it.
101+
if (jsonTypeInfo.CreateObject is null && TypeToConvert.IsAssignableFrom(typeof(Dictionary<string, object?>)))
102+
{
103+
Debug.Assert(TypeToConvert.IsInterface);
104+
jsonTypeInfo.CreateObject = () => new Dictionary<string, object?>();
105+
}
106+
}
119107
}
120108
}

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

Lines changed: 11 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
// The .NET Foundation licenses this file to you under the MIT license.
33

44
using System.Collections.Generic;
5+
using System.Diagnostics;
56
using System.Text.Json.Serialization.Metadata;
67

78
namespace System.Text.Json.Serialization.Converters
@@ -27,45 +28,22 @@ protected override void Add(TKey key, in TValue value, JsonSerializerOptions opt
2728

2829
protected override void CreateCollection(ref Utf8JsonReader reader, ref ReadStack state)
2930
{
30-
JsonTypeInfo typeInfo = state.Current.JsonTypeInfo;
31-
32-
if (TypeToConvert.IsInterface || TypeToConvert.IsAbstract)
33-
{
34-
if (!TypeToConvert.IsAssignableFrom(RuntimeType))
35-
{
36-
ThrowHelper.ThrowNotSupportedException_CannotPopulateCollection(TypeToConvert, ref reader, ref state);
37-
}
38-
39-
state.Current.ReturnValue = new Dictionary<TKey, TValue>();
40-
}
41-
else
31+
base.CreateCollection(ref reader, ref state);
32+
TDictionary returnValue = (TDictionary)state.Current.ReturnValue!;
33+
if (returnValue.IsReadOnly)
4234
{
43-
if (typeInfo.CreateObject == null)
44-
{
45-
ThrowHelper.ThrowNotSupportedException_DeserializeNoConstructor(TypeToConvert, ref reader, ref state);
46-
}
47-
48-
TDictionary returnValue = (TDictionary)typeInfo.CreateObject()!;
49-
50-
if (returnValue.IsReadOnly)
51-
{
52-
ThrowHelper.ThrowNotSupportedException_CannotPopulateCollection(TypeToConvert, ref reader, ref state);
53-
}
54-
55-
state.Current.ReturnValue = returnValue;
35+
state.Current.ReturnValue = null; // clear out for more accurate JsonPath reporting.
36+
ThrowHelper.ThrowNotSupportedException_CannotPopulateCollection(TypeToConvert, ref reader, ref state);
5637
}
5738
}
5839

59-
internal override Type RuntimeType
40+
internal override void ConfigureJsonTypeInfo(JsonTypeInfo jsonTypeInfo, JsonSerializerOptions options)
6041
{
61-
get
42+
// Deserialize as Dictionary<TKey,TValue> for interface types that support it.
43+
if (jsonTypeInfo.CreateObject is null && TypeToConvert.IsAssignableFrom(typeof(Dictionary<TKey, TValue>)))
6244
{
63-
if (TypeToConvert.IsAbstract || TypeToConvert.IsInterface)
64-
{
65-
return typeof(Dictionary<TKey, TValue>);
66-
}
67-
68-
return TypeToConvert;
45+
Debug.Assert(TypeToConvert.IsInterface);
46+
jsonTypeInfo.CreateObject = () => new Dictionary<TKey, TValue>();
6947
}
7048
}
7149
}

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

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33

44
using System.Collections;
55
using System.Collections.Generic;
6+
using System.Text.Json.Serialization.Metadata;
67

78
namespace System.Text.Json.Serialization.Converters
89
{
@@ -14,14 +15,16 @@ internal sealed class IEnumerableConverter<TCollection>
1415
: JsonCollectionConverter<TCollection, object?>
1516
where TCollection : IEnumerable
1617
{
18+
private readonly bool _isDeserializable = typeof(TCollection).IsAssignableFrom(typeof(List<object?>));
19+
1720
protected override void Add(in object? value, ref ReadStack state)
1821
{
1922
((List<object?>)state.Current.ReturnValue!).Add(value);
2023
}
2124

2225
protected override void CreateCollection(ref Utf8JsonReader reader, ref ReadStack state, JsonSerializerOptions options)
2326
{
24-
if (!TypeToConvert.IsAssignableFrom(RuntimeType))
27+
if (!_isDeserializable)
2528
{
2629
ThrowHelper.ThrowNotSupportedException_CannotPopulateCollection(TypeToConvert, ref reader, ref state);
2730
}
@@ -71,7 +74,5 @@ protected override bool OnWriteResume(
7174

7275
return true;
7376
}
74-
75-
internal override Type RuntimeType => typeof(List<object?>);
7677
}
7778
}

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

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -13,21 +13,21 @@ internal sealed class IEnumerableOfTConverter<TCollection, TElement>
1313
: IEnumerableDefaultConverter<TCollection, TElement>
1414
where TCollection : IEnumerable<TElement>
1515
{
16+
private readonly bool _isDeserializable = typeof(TCollection).IsAssignableFrom(typeof(List<TElement>));
17+
1618
protected override void Add(in TElement value, ref ReadStack state)
1719
{
1820
((List<TElement>)state.Current.ReturnValue!).Add(value);
1921
}
2022

2123
protected override void CreateCollection(ref Utf8JsonReader reader, ref ReadStack state, JsonSerializerOptions options)
2224
{
23-
if (!TypeToConvert.IsAssignableFrom(RuntimeType))
25+
if (!_isDeserializable)
2426
{
2527
ThrowHelper.ThrowNotSupportedException_CannotPopulateCollection(TypeToConvert, ref reader, ref state);
2628
}
2729

2830
state.Current.ReturnValue = new List<TElement>();
2931
}
30-
31-
internal override Type RuntimeType => typeof(List<TElement>);
3232
}
3333
}

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

Lines changed: 12 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33

44
using System.Collections;
55
using System.Collections.Generic;
6+
using System.Diagnostics;
67
using System.Text.Json.Serialization.Metadata;
78

89
namespace System.Text.Json.Serialization.Converters
@@ -19,37 +20,17 @@ protected override void Add(in object? value, ref ReadStack state)
1920
if (IsValueType)
2021
{
2122
state.Current.ReturnValue = collection;
22-
};
23+
}
2324
}
2425

2526
protected override void CreateCollection(ref Utf8JsonReader reader, ref ReadStack state, JsonSerializerOptions options)
2627
{
27-
JsonTypeInfo typeInfo = state.Current.JsonTypeInfo;
28-
29-
if (TypeToConvert.IsInterface || TypeToConvert.IsAbstract)
28+
base.CreateCollection(ref reader, ref state, options);
29+
TCollection returnValue = (TCollection)state.Current.ReturnValue!;
30+
if (returnValue.IsReadOnly)
3031
{
31-
if (!TypeToConvert.IsAssignableFrom(RuntimeType))
32-
{
33-
ThrowHelper.ThrowNotSupportedException_CannotPopulateCollection(TypeToConvert, ref reader, ref state);
34-
}
35-
36-
state.Current.ReturnValue = new List<object?>();
37-
}
38-
else
39-
{
40-
if (typeInfo.CreateObject == null)
41-
{
42-
ThrowHelper.ThrowNotSupportedException_DeserializeNoConstructor(TypeToConvert, ref reader, ref state);
43-
}
44-
45-
TCollection returnValue = (TCollection)typeInfo.CreateObject()!;
46-
47-
if (returnValue.IsReadOnly)
48-
{
49-
ThrowHelper.ThrowNotSupportedException_CannotPopulateCollection(TypeToConvert, ref reader, ref state);
50-
}
51-
52-
state.Current.ReturnValue = returnValue;
32+
state.Current.ReturnValue = null; // clear out for more accurate JsonPath reporting.
33+
ThrowHelper.ThrowNotSupportedException_CannotPopulateCollection(TypeToConvert, ref reader, ref state);
5334
}
5435
}
5536

@@ -91,16 +72,13 @@ protected override bool OnWriteResume(Utf8JsonWriter writer, TCollection value,
9172
return true;
9273
}
9374

94-
internal override Type RuntimeType
75+
internal override void ConfigureJsonTypeInfo(JsonTypeInfo jsonTypeInfo, JsonSerializerOptions options)
9576
{
96-
get
77+
// Deserialize as List<object?> for interface types that support it.
78+
if (jsonTypeInfo.CreateObject is null && TypeToConvert.IsAssignableFrom(typeof(List<object?>)))
9779
{
98-
if (TypeToConvert.IsAbstract || TypeToConvert.IsInterface)
99-
{
100-
return typeof(List<object?>);
101-
}
102-
103-
return TypeToConvert;
80+
Debug.Assert(TypeToConvert.IsInterface);
81+
jsonTypeInfo.CreateObject = () => new List<object?>();
10482
}
10583
}
10684
}

0 commit comments

Comments
 (0)