未验证 提交 8356c8a3 编写于 作者: E Eirik Tsarpalis 提交者: GitHub

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.
上级 88714202
......@@ -2,7 +2,6 @@
// The .NET Foundation licenses this file to you under the MIT license.
using System.Collections.Concurrent;
using System.Collections.Generic;
namespace System.Text.Json.Serialization.Converters
{
......@@ -14,15 +13,5 @@ protected override void Add(in TElement value, ref ReadStack state)
{
((TCollection)state.Current.ReturnValue!).Enqueue(value);
}
protected override void CreateCollection(ref Utf8JsonReader reader, ref ReadStack state, JsonSerializerOptions options)
{
if (state.Current.JsonTypeInfo.CreateObject is null)
{
ThrowHelper.ThrowNotSupportedException_SerializationNotSupported(state.Current.JsonTypeInfo.Type);
}
state.Current.ReturnValue = state.Current.JsonTypeInfo.CreateObject();
}
}
}
......@@ -2,7 +2,6 @@
// The .NET Foundation licenses this file to you under the MIT license.
using System.Collections.Concurrent;
using System.Collections.Generic;
namespace System.Text.Json.Serialization.Converters
{
......@@ -14,15 +13,5 @@ protected override void Add(in TElement value, ref ReadStack state)
{
((TCollection)state.Current.ReturnValue!).Push(value);
}
protected override void CreateCollection(ref Utf8JsonReader reader, ref ReadStack state, JsonSerializerOptions options)
{
if (state.Current.JsonTypeInfo.CreateObject is null)
{
ThrowHelper.ThrowNotSupportedException_SerializationNotSupported(state.Current.JsonTypeInfo.Type);
}
state.Current.ReturnValue = state.Current.JsonTypeInfo.CreateObject();
}
}
}
......@@ -20,16 +20,6 @@ protected override void Add(TKey key, in TValue value, JsonSerializerOptions opt
((TCollection)state.Current.ReturnValue!)[key] = value;
}
protected override void CreateCollection(ref Utf8JsonReader reader, ref ReadStack state)
{
if (state.Current.JsonTypeInfo.CreateObject == null)
{
ThrowHelper.ThrowNotSupportedException_SerializationNotSupported(state.Current.JsonTypeInfo.Type);
}
state.Current.ReturnValue = state.Current.JsonTypeInfo.CreateObject();
}
protected internal override bool OnWriteResume(
Utf8JsonWriter writer,
TCollection value,
......
......@@ -2,6 +2,7 @@
// The .NET Foundation licenses this file to you under the MIT license.
using System.Collections.Generic;
using System.Diagnostics;
using System.Text.Json.Serialization.Metadata;
namespace System.Text.Json.Serialization.Converters
......@@ -25,45 +26,22 @@ protected override void Add(in TElement value, ref ReadStack state)
protected override void CreateCollection(ref Utf8JsonReader reader, ref ReadStack state, JsonSerializerOptions options)
{
JsonTypeInfo typeInfo = state.Current.JsonTypeInfo;
if (TypeToConvert.IsInterface || TypeToConvert.IsAbstract)
{
if (!TypeToConvert.IsAssignableFrom(RuntimeType))
{
ThrowHelper.ThrowNotSupportedException_CannotPopulateCollection(TypeToConvert, ref reader, ref state);
}
state.Current.ReturnValue = new List<TElement>();
}
else
base.CreateCollection(ref reader, ref state, options);
TCollection returnValue = (TCollection)state.Current.ReturnValue!;
if (returnValue.IsReadOnly)
{
if (typeInfo.CreateObject == null)
{
ThrowHelper.ThrowNotSupportedException_DeserializeNoConstructor(TypeToConvert, ref reader, ref state);
}
TCollection returnValue = (TCollection)typeInfo.CreateObject()!;
if (returnValue.IsReadOnly)
{
ThrowHelper.ThrowNotSupportedException_CannotPopulateCollection(TypeToConvert, ref reader, ref state);
}
state.Current.ReturnValue = returnValue;
state.Current.ReturnValue = null; // clear out for more accurate JsonPath reporting.
ThrowHelper.ThrowNotSupportedException_CannotPopulateCollection(TypeToConvert, ref reader, ref state);
}
}
internal override Type RuntimeType
internal override void ConfigureJsonTypeInfo(JsonTypeInfo jsonTypeInfo, JsonSerializerOptions options)
{
get
// Deserialize as List<T> for interface types that support it.
if (jsonTypeInfo.CreateObject is null && TypeToConvert.IsAssignableFrom(typeof(List<TElement>)))
{
if (TypeToConvert.IsAbstract || TypeToConvert.IsInterface)
{
return typeof(List<TElement>);
}
return TypeToConvert;
Debug.Assert(TypeToConvert.IsInterface);
jsonTypeInfo.CreateObject = () => new List<TElement>();
}
}
}
......
......@@ -3,6 +3,7 @@
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.Text.Json.Serialization.Metadata;
namespace System.Text.Json.Serialization.Converters
......@@ -27,33 +28,12 @@ protected override void Add(string key, in object? value, JsonSerializerOptions
protected override void CreateCollection(ref Utf8JsonReader reader, ref ReadStack state)
{
JsonTypeInfo typeInfo = state.Current.JsonTypeInfo;
if (TypeToConvert.IsInterface || TypeToConvert.IsAbstract)
base.CreateCollection(ref reader, ref state);
TDictionary returnValue = (TDictionary)state.Current.ReturnValue!;
if (returnValue.IsReadOnly)
{
if (!TypeToConvert.IsAssignableFrom(RuntimeType))
{
ThrowHelper.ThrowNotSupportedException_CannotPopulateCollection(TypeToConvert, ref reader, ref state);
}
// Strings are intentionally used as keys when deserializing non-generic dictionaries.
state.Current.ReturnValue = new Dictionary<string, object>();
}
else
{
if (typeInfo.CreateObject is null)
{
ThrowHelper.ThrowNotSupportedException_DeserializeNoConstructor(TypeToConvert, ref reader, ref state);
}
TDictionary returnValue = (TDictionary)typeInfo.CreateObject()!;
if (returnValue.IsReadOnly)
{
ThrowHelper.ThrowNotSupportedException_CannotPopulateCollection(TypeToConvert, ref reader, ref state);
}
state.Current.ReturnValue = returnValue;
state.Current.ReturnValue = null; // clear out for more accurate JsonPath reporting.
ThrowHelper.ThrowNotSupportedException_CannotPopulateCollection(TypeToConvert, ref reader, ref state);
}
}
......@@ -115,6 +95,14 @@ protected internal override bool OnWriteResume(Utf8JsonWriter writer, TDictionar
return true;
}
internal override Type RuntimeType => TypeToConvert.IsAbstract || TypeToConvert.IsInterface ? typeof(Dictionary<string, object>) : TypeToConvert;
internal override void ConfigureJsonTypeInfo(JsonTypeInfo jsonTypeInfo, JsonSerializerOptions options)
{
// Deserialize as Dictionary<TKey,TValue> for interface types that support it.
if (jsonTypeInfo.CreateObject is null && TypeToConvert.IsAssignableFrom(typeof(Dictionary<string, object?>)))
{
Debug.Assert(TypeToConvert.IsInterface);
jsonTypeInfo.CreateObject = () => new Dictionary<string, object?>();
}
}
}
}
......@@ -2,6 +2,7 @@
// The .NET Foundation licenses this file to you under the MIT license.
using System.Collections.Generic;
using System.Diagnostics;
using System.Text.Json.Serialization.Metadata;
namespace System.Text.Json.Serialization.Converters
......@@ -27,45 +28,22 @@ protected override void Add(TKey key, in TValue value, JsonSerializerOptions opt
protected override void CreateCollection(ref Utf8JsonReader reader, ref ReadStack state)
{
JsonTypeInfo typeInfo = state.Current.JsonTypeInfo;
if (TypeToConvert.IsInterface || TypeToConvert.IsAbstract)
{
if (!TypeToConvert.IsAssignableFrom(RuntimeType))
{
ThrowHelper.ThrowNotSupportedException_CannotPopulateCollection(TypeToConvert, ref reader, ref state);
}
state.Current.ReturnValue = new Dictionary<TKey, TValue>();
}
else
base.CreateCollection(ref reader, ref state);
TDictionary returnValue = (TDictionary)state.Current.ReturnValue!;
if (returnValue.IsReadOnly)
{
if (typeInfo.CreateObject == null)
{
ThrowHelper.ThrowNotSupportedException_DeserializeNoConstructor(TypeToConvert, ref reader, ref state);
}
TDictionary returnValue = (TDictionary)typeInfo.CreateObject()!;
if (returnValue.IsReadOnly)
{
ThrowHelper.ThrowNotSupportedException_CannotPopulateCollection(TypeToConvert, ref reader, ref state);
}
state.Current.ReturnValue = returnValue;
state.Current.ReturnValue = null; // clear out for more accurate JsonPath reporting.
ThrowHelper.ThrowNotSupportedException_CannotPopulateCollection(TypeToConvert, ref reader, ref state);
}
}
internal override Type RuntimeType
internal override void ConfigureJsonTypeInfo(JsonTypeInfo jsonTypeInfo, JsonSerializerOptions options)
{
get
// Deserialize as Dictionary<TKey,TValue> for interface types that support it.
if (jsonTypeInfo.CreateObject is null && TypeToConvert.IsAssignableFrom(typeof(Dictionary<TKey, TValue>)))
{
if (TypeToConvert.IsAbstract || TypeToConvert.IsInterface)
{
return typeof(Dictionary<TKey, TValue>);
}
return TypeToConvert;
Debug.Assert(TypeToConvert.IsInterface);
jsonTypeInfo.CreateObject = () => new Dictionary<TKey, TValue>();
}
}
}
......
......@@ -3,6 +3,7 @@
using System.Collections;
using System.Collections.Generic;
using System.Text.Json.Serialization.Metadata;
namespace System.Text.Json.Serialization.Converters
{
......@@ -14,6 +15,8 @@ internal sealed class IEnumerableConverter<TCollection>
: JsonCollectionConverter<TCollection, object?>
where TCollection : IEnumerable
{
private readonly bool _isDeserializable = typeof(TCollection).IsAssignableFrom(typeof(List<object?>));
protected override void Add(in object? value, ref ReadStack state)
{
((List<object?>)state.Current.ReturnValue!).Add(value);
......@@ -21,7 +24,7 @@ protected override void Add(in object? value, ref ReadStack state)
protected override void CreateCollection(ref Utf8JsonReader reader, ref ReadStack state, JsonSerializerOptions options)
{
if (!TypeToConvert.IsAssignableFrom(RuntimeType))
if (!_isDeserializable)
{
ThrowHelper.ThrowNotSupportedException_CannotPopulateCollection(TypeToConvert, ref reader, ref state);
}
......@@ -71,7 +74,5 @@ protected override void CreateCollection(ref Utf8JsonReader reader, ref ReadStac
return true;
}
internal override Type RuntimeType => typeof(List<object?>);
}
}
......@@ -13,6 +13,8 @@ internal sealed class IEnumerableOfTConverter<TCollection, TElement>
: IEnumerableDefaultConverter<TCollection, TElement>
where TCollection : IEnumerable<TElement>
{
private readonly bool _isDeserializable = typeof(TCollection).IsAssignableFrom(typeof(List<TElement>));
protected override void Add(in TElement value, ref ReadStack state)
{
((List<TElement>)state.Current.ReturnValue!).Add(value);
......@@ -20,14 +22,12 @@ protected override void Add(in TElement value, ref ReadStack state)
protected override void CreateCollection(ref Utf8JsonReader reader, ref ReadStack state, JsonSerializerOptions options)
{
if (!TypeToConvert.IsAssignableFrom(RuntimeType))
if (!_isDeserializable)
{
ThrowHelper.ThrowNotSupportedException_CannotPopulateCollection(TypeToConvert, ref reader, ref state);
}
state.Current.ReturnValue = new List<TElement>();
}
internal override Type RuntimeType => typeof(List<TElement>);
}
}
......@@ -3,6 +3,7 @@
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.Text.Json.Serialization.Metadata;
namespace System.Text.Json.Serialization.Converters
......@@ -19,37 +20,17 @@ protected override void Add(in object? value, ref ReadStack state)
if (IsValueType)
{
state.Current.ReturnValue = collection;
};
}
}
protected override void CreateCollection(ref Utf8JsonReader reader, ref ReadStack state, JsonSerializerOptions options)
{
JsonTypeInfo typeInfo = state.Current.JsonTypeInfo;
if (TypeToConvert.IsInterface || TypeToConvert.IsAbstract)
base.CreateCollection(ref reader, ref state, options);
TCollection returnValue = (TCollection)state.Current.ReturnValue!;
if (returnValue.IsReadOnly)
{
if (!TypeToConvert.IsAssignableFrom(RuntimeType))
{
ThrowHelper.ThrowNotSupportedException_CannotPopulateCollection(TypeToConvert, ref reader, ref state);
}
state.Current.ReturnValue = new List<object?>();
}
else
{
if (typeInfo.CreateObject == null)
{
ThrowHelper.ThrowNotSupportedException_DeserializeNoConstructor(TypeToConvert, ref reader, ref state);
}
TCollection returnValue = (TCollection)typeInfo.CreateObject()!;
if (returnValue.IsReadOnly)
{
ThrowHelper.ThrowNotSupportedException_CannotPopulateCollection(TypeToConvert, ref reader, ref state);
}
state.Current.ReturnValue = returnValue;
state.Current.ReturnValue = null; // clear out for more accurate JsonPath reporting.
ThrowHelper.ThrowNotSupportedException_CannotPopulateCollection(TypeToConvert, ref reader, ref state);
}
}
......@@ -91,16 +72,13 @@ protected override bool OnWriteResume(Utf8JsonWriter writer, TCollection value,
return true;
}
internal override Type RuntimeType
internal override void ConfigureJsonTypeInfo(JsonTypeInfo jsonTypeInfo, JsonSerializerOptions options)
{
get
// Deserialize as List<object?> for interface types that support it.
if (jsonTypeInfo.CreateObject is null && TypeToConvert.IsAssignableFrom(typeof(List<object?>)))
{
if (TypeToConvert.IsAbstract || TypeToConvert.IsInterface)
{
return typeof(List<object?>);
}
return TypeToConvert;
Debug.Assert(TypeToConvert.IsInterface);
jsonTypeInfo.CreateObject = () => new List<object?>();
}
}
}
......
......@@ -2,6 +2,7 @@
// The .NET Foundation licenses this file to you under the MIT license.
using System.Collections.Generic;
using System.Diagnostics;
using System.Text.Json.Serialization.Metadata;
namespace System.Text.Json.Serialization.Converters
......@@ -25,45 +26,22 @@ protected override void Add(in TElement value, ref ReadStack state)
protected override void CreateCollection(ref Utf8JsonReader reader, ref ReadStack state, JsonSerializerOptions options)
{
JsonTypeInfo typeInfo = state.Current.JsonTypeInfo;
if (TypeToConvert.IsInterface || TypeToConvert.IsAbstract)
{
if (!TypeToConvert.IsAssignableFrom(RuntimeType))
{
ThrowHelper.ThrowNotSupportedException_CannotPopulateCollection(TypeToConvert, ref reader, ref state);
}
state.Current.ReturnValue = new List<TElement>();
}
else
base.CreateCollection(ref reader, ref state, options);
TCollection returnValue = (TCollection)state.Current.ReturnValue!;
if (returnValue.IsReadOnly)
{
if (typeInfo.CreateObject == null)
{
ThrowHelper.ThrowNotSupportedException_DeserializeNoConstructor(TypeToConvert, ref reader, ref state);
}
TCollection returnValue = (TCollection)typeInfo.CreateObject()!;
if (returnValue.IsReadOnly)
{
ThrowHelper.ThrowNotSupportedException_CannotPopulateCollection(TypeToConvert, ref reader, ref state);
}
state.Current.ReturnValue = returnValue;
state.Current.ReturnValue = null; // clear out for more accurate JsonPath reporting.
ThrowHelper.ThrowNotSupportedException_CannotPopulateCollection(TypeToConvert, ref reader, ref state);
}
}
internal override Type RuntimeType
internal override void ConfigureJsonTypeInfo(JsonTypeInfo jsonTypeInfo, JsonSerializerOptions options)
{
get
// Deserialize as List<T> for interface types that support it.
if (jsonTypeInfo.CreateObject is null && TypeToConvert.IsAssignableFrom(typeof(List<TElement>)))
{
if (TypeToConvert.IsAbstract || TypeToConvert.IsInterface)
{
return typeof(List<TElement>);
}
return TypeToConvert;
Debug.Assert(TypeToConvert.IsInterface);
jsonTypeInfo.CreateObject = () => new List<TElement>();
}
}
}
......
......@@ -11,6 +11,8 @@ internal sealed class IReadOnlyDictionaryOfTKeyTValueConverter<TDictionary, TKey
where TDictionary : IReadOnlyDictionary<TKey, TValue>
where TKey : notnull
{
private readonly bool _isDeserializable = typeof(TDictionary).IsAssignableFrom(typeof(Dictionary<TKey, TValue>));
protected override void Add(TKey key, in TValue value, JsonSerializerOptions options, ref ReadStack state)
{
((Dictionary<TKey, TValue>)state.Current.ReturnValue!)[key] = value;
......@@ -18,14 +20,12 @@ protected override void Add(TKey key, in TValue value, JsonSerializerOptions opt
protected override void CreateCollection(ref Utf8JsonReader reader, ref ReadStack state)
{
if (!TypeToConvert.IsAssignableFrom(RuntimeType))
if (!_isDeserializable)
{
ThrowHelper.ThrowNotSupportedException_CannotPopulateCollection(TypeToConvert, ref reader, ref state);
}
state.Current.ReturnValue = new Dictionary<TKey, TValue>();
}
internal override Type RuntimeType => typeof(Dictionary<TKey, TValue>);
}
}
......@@ -2,6 +2,7 @@
// The .NET Foundation licenses this file to you under the MIT license.
using System.Collections.Generic;
using System.Diagnostics;
using System.Text.Json.Serialization.Metadata;
namespace System.Text.Json.Serialization.Converters
......@@ -22,45 +23,22 @@ protected override void Add(in TElement value, ref ReadStack state)
protected override void CreateCollection(ref Utf8JsonReader reader, ref ReadStack state, JsonSerializerOptions options)
{
JsonTypeInfo typeInfo = state.Current.JsonTypeInfo;
if (TypeToConvert.IsInterface || TypeToConvert.IsAbstract)
{
if (!TypeToConvert.IsAssignableFrom(RuntimeType))
{
ThrowHelper.ThrowNotSupportedException_CannotPopulateCollection(TypeToConvert, ref reader, ref state);
}
state.Current.ReturnValue = new HashSet<TElement>();
}
else
base.CreateCollection(ref reader, ref state, options);
TCollection returnValue = (TCollection)state.Current.ReturnValue!;
if (returnValue.IsReadOnly)
{
if (typeInfo.CreateObject == null)
{
ThrowHelper.ThrowNotSupportedException_DeserializeNoConstructor(TypeToConvert, ref reader, ref state);
}
TCollection returnValue = (TCollection)typeInfo.CreateObject()!;
if (returnValue.IsReadOnly)
{
ThrowHelper.ThrowNotSupportedException_CannotPopulateCollection(TypeToConvert, ref reader, ref state);
}
state.Current.ReturnValue = returnValue;
state.Current.ReturnValue = null; // clear out for more accurate JsonPath reporting.
ThrowHelper.ThrowNotSupportedException_CannotPopulateCollection(TypeToConvert, ref reader, ref state);
}
}
internal override Type RuntimeType
internal override void ConfigureJsonTypeInfo(JsonTypeInfo jsonTypeInfo, JsonSerializerOptions options)
{
get
// Deserialize as HashSet<TElement> for interface types that support it.
if (jsonTypeInfo.CreateObject is null && TypeToConvert.IsAssignableFrom(typeof(HashSet<TElement>)))
{
if (TypeToConvert.IsAbstract || TypeToConvert.IsInterface)
{
return typeof(HashSet<TElement>);
}
return TypeToConvert;
Debug.Assert(TypeToConvert.IsInterface);
jsonTypeInfo.CreateObject = () => new HashSet<TElement>();
}
}
}
......
......@@ -18,13 +18,9 @@ public ImmutableDictionaryOfTKeyTValueConverterWithReflection()
{
}
internal override bool RequiresDynamicMemberAccessors => true;
[UnconditionalSuppressMessage("ReflectionAnalysis", "IL2026:RequiresUnreferencedCode",
Justification = "The ctor is marked RequiresUnreferencedCode.")]
internal override void Initialize(JsonSerializerOptions options, JsonTypeInfo? jsonTypeInfo = null)
[RequiresUnreferencedCode(IEnumerableConverterFactoryHelpers.ImmutableConvertersUnreferencedCodeMessage)]
internal override void ConfigureJsonTypeInfoUsingReflection(JsonTypeInfo jsonTypeInfo, JsonSerializerOptions options)
{
Debug.Assert(jsonTypeInfo != null);
jsonTypeInfo.CreateObjectWithArgs = options.MemberAccessorStrategy.CreateImmutableDictionaryCreateRangeDelegate<TCollection, TKey, TValue>();
}
}
......
......@@ -17,13 +17,9 @@ public ImmutableEnumerableOfTConverterWithReflection()
{
}
internal override bool RequiresDynamicMemberAccessors => true;
[UnconditionalSuppressMessage("ReflectionAnalysis", "IL2026:RequiresUnreferencedCode",
Justification = "The ctor is marked RequiresUnreferencedCode.")]
internal override void Initialize(JsonSerializerOptions options, JsonTypeInfo? jsonTypeInfo = null)
[RequiresUnreferencedCode(IEnumerableConverterFactoryHelpers.ImmutableConvertersUnreferencedCodeMessage)]
internal override void ConfigureJsonTypeInfoUsingReflection(JsonTypeInfo jsonTypeInfo, JsonSerializerOptions options)
{
Debug.Assert(jsonTypeInfo != null);
jsonTypeInfo.CreateObjectWithArgs = options.MemberAccessorStrategy.CreateImmutableEnumerableCreateRangeDelegate<TCollection, TElement>();
}
}
......
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.Diagnostics.CodeAnalysis;
......@@ -20,7 +18,33 @@ internal abstract class JsonCollectionConverter<TCollection, TElement> : JsonRes
internal override Type ElementType => typeof(TElement);
protected abstract void Add(in TElement value, ref ReadStack state);
protected abstract void CreateCollection(ref Utf8JsonReader reader, ref ReadStack state, JsonSerializerOptions options);
/// <summary>
/// When overridden, create the collection. It may be a temporary collection or the final collection.
/// </summary>
protected virtual void CreateCollection(ref Utf8JsonReader reader, ref ReadStack state, JsonSerializerOptions options)
{
JsonTypeInfo typeInfo = state.Current.JsonTypeInfo;
if (typeInfo.CreateObject is null)
{
// The contract model was not able to produce a default constructor for two possible reasons:
// 1. Either the declared collection type is abstract and cannot be instantiated.
// 2. The collection type does not specify a default constructor.
if (TypeToConvert.IsAbstract || TypeToConvert.IsInterface)
{
ThrowHelper.ThrowNotSupportedException_CannotPopulateCollection(TypeToConvert, ref reader, ref state);
}
else
{
ThrowHelper.ThrowNotSupportedException_DeserializeNoConstructor(TypeToConvert, ref reader, ref state);
}
}
state.Current.ReturnValue = typeInfo.CreateObject()!;
Debug.Assert(state.Current.ReturnValue is TCollection);
}
protected virtual void ConvertCollection(ref ReadStack state, JsonSerializerOptions options) { }
protected static JsonConverter<TElement> GetElementConverter(JsonTypeInfo elementTypeInfo)
......
......@@ -37,7 +37,28 @@ internal abstract class JsonDictionaryConverter<TDictionary, TKey, TValue> : Jso
/// <summary>
/// When overridden, create the collection. It may be a temporary collection or the final collection.
/// </summary>
protected virtual void CreateCollection(ref Utf8JsonReader reader, ref ReadStack state) { }
protected virtual void CreateCollection(ref Utf8JsonReader reader, ref ReadStack state)
{
JsonTypeInfo typeInfo = state.Current.JsonTypeInfo;
if (typeInfo.CreateObject is null)
{
// The contract model was not able to produce a default constructor for two possible reasons:
// 1. Either the declared collection type is abstract and cannot be instantiated.
// 2. The collection type does not specify a default constructor.
if (TypeToConvert.IsAbstract || TypeToConvert.IsInterface)
{
ThrowHelper.ThrowNotSupportedException_CannotPopulateCollection(TypeToConvert, ref reader, ref state);
}
else
{
ThrowHelper.ThrowNotSupportedException_DeserializeNoConstructor(TypeToConvert, ref reader, ref state);
}
}
state.Current.ReturnValue = typeInfo.CreateObject()!;
Debug.Assert(state.Current.ReturnValue is TDictionary);
}
internal override Type ElementType => typeof(TValue);
......
......@@ -12,16 +12,12 @@ internal sealed class StackOrQueueConverterWithReflection<TCollection>
: StackOrQueueConverter<TCollection>
where TCollection : IEnumerable
{
internal override bool RequiresDynamicMemberAccessors => true;
[RequiresUnreferencedCode(JsonSerializer.SerializationUnreferencedCodeMessage)]
public StackOrQueueConverterWithReflection() { }
[UnconditionalSuppressMessage("ReflectionAnalysis", "IL2091:UnrecognizedReflectionPattern",
Justification = "The ctor is marked RequiresUnreferencedCode.")]
internal override void Initialize(JsonSerializerOptions options, JsonTypeInfo? jsonTypeInfo = null)
[RequiresUnreferencedCode(JsonSerializer.SerializationUnreferencedCodeMessage)]
internal override void ConfigureJsonTypeInfoUsingReflection(JsonTypeInfo jsonTypeInfo, JsonSerializerOptions options)
{
Debug.Assert(jsonTypeInfo != null);
jsonTypeInfo.AddMethodDelegate = options.MemberAccessorStrategy.CreateAddMethodDelegate<TCollection>();
}
}
......
......@@ -89,5 +89,8 @@ internal override bool OnTryWrite(Utf8JsonWriter writer, T value, JsonSerializer
return Converter.OnTryWrite(writer, value, options, ref state);
}
internal override void ConfigureJsonTypeInfo(JsonTypeInfo jsonTypeInfo, JsonSerializerOptions options)
=> Converter.ConfigureJsonTypeInfo(jsonTypeInfo, options);
}
}
......@@ -19,13 +19,9 @@ public LargeObjectWithParameterizedConstructorConverterWithReflection()
{
}
internal override bool RequiresDynamicMemberAccessors => true;
[UnconditionalSuppressMessage("ReflectionAnalysis", "IL2026:RequiresUnreferencedCode",
Justification = "The ctor is marked RequiresUnreferencedCode.")]
internal override void Initialize(JsonSerializerOptions options, JsonTypeInfo? jsonTypeInfo = null)
[RequiresUnreferencedCode(JsonSerializer.SerializationUnreferencedCodeMessage)]
internal override void ConfigureJsonTypeInfoUsingReflection(JsonTypeInfo jsonTypeInfo, JsonSerializerOptions options)
{
Debug.Assert(jsonTypeInfo != null);
jsonTypeInfo.CreateObjectWithArgs = options.MemberAccessorStrategy.CreateParameterizedConstructor<T>(ConstructorInfo!);
}
}
......
......@@ -65,7 +65,7 @@ protected override object CreateObject(ref ReadStackFrame frame)
var info = (JsonParameterInfo<TArg>)jsonParameterInfo;
var converter = (JsonConverter<TArg>)jsonParameterInfo.ConverterBase;
bool success = converter.TryRead(ref reader, info.RuntimePropertyType, info.Options!, ref state, out TArg? value);
bool success = converter.TryRead(ref reader, info.PropertyType, info.Options!, ref state, out TArg? value);
arg = value == null && jsonParameterInfo.IgnoreDefaultValuesOnRead
? (TArg?)info.DefaultValue! // Use default value specified on parameter, if any.
......
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
using System.Diagnostics.CodeAnalysis;
using System.Reflection;
using System.Text.Json.Serialization.Metadata;
......@@ -83,8 +84,6 @@ internal virtual object CreateObject(JsonSerializerOptions options)
/// </summary>
internal abstract object? ReadCoreAsObject(ref Utf8JsonReader reader, JsonSerializerOptions options, ref ReadStack state);
// For polymorphic cases, the concrete type to create.
internal virtual Type RuntimeType => TypeToConvert;
internal bool ShouldFlush(Utf8JsonWriter writer, ref WriteStack state)
{
......@@ -114,9 +113,16 @@ internal bool ShouldFlush(Utf8JsonWriter writer, ref WriteStack state)
internal ConstructorInfo? ConstructorInfo { get; set; }
internal virtual bool RequiresDynamicMemberAccessors { get; }
/// <summary>
/// Used for hooking custom configuration to a newly created associated JsonTypeInfo instance.
/// </summary>
internal virtual void ConfigureJsonTypeInfo(JsonTypeInfo jsonTypeInfo, JsonSerializerOptions options) { }
internal virtual void Initialize(JsonSerializerOptions options, JsonTypeInfo? jsonTypeInfo = null) { }
/// <summary>
/// Additional reflection-specific configuration required by certain collection converters.
/// </summary>
[RequiresUnreferencedCode(JsonSerializer.SerializationUnreferencedCodeMessage)]
internal virtual void ConfigureJsonTypeInfoUsingReflection(JsonTypeInfo jsonTypeInfo, JsonSerializerOptions options) { }
/// <summary>
/// Creates the instance and assigns it to state.Current.ReturnValue.
......
......@@ -59,7 +59,7 @@ public partial class JsonConverter<T>
}
JsonPropertyInfo jsonPropertyInfo = state.Current.JsonTypeInfo.PropertyInfoForTypeInfo;
bool success = TryRead(ref reader, jsonPropertyInfo.RuntimePropertyType!, options, ref state, out T? value);
bool success = TryRead(ref reader, jsonPropertyInfo.PropertyType, options, ref state, out T? value);
if (success)
{
// Read any trailing whitespace. This will throw if JsonCommentHandling=Disallow.
......
......@@ -105,7 +105,7 @@ public static partial class JsonSerializer
{
// Create the appropriate dictionary type. We already verified the types.
#if DEBUG
Type underlyingIDictionaryType = jsonPropertyInfo.DeclaredPropertyType.GetCompatibleGenericInterface(typeof(IDictionary<,>))!;
Type underlyingIDictionaryType = jsonPropertyInfo.PropertyType.GetCompatibleGenericInterface(typeof(IDictionary<,>))!;
Type[] genericArgs = underlyingIDictionaryType.GetGenericArguments();
Debug.Assert(underlyingIDictionaryType.IsGenericType);
......@@ -116,21 +116,21 @@ public static partial class JsonSerializer
genericArgs[1].UnderlyingSystemType == typeof(JsonElement) ||
genericArgs[1].UnderlyingSystemType == typeof(Nodes.JsonNode));
#endif
if (jsonPropertyInfo.RuntimeTypeInfo.CreateObject == null)
if (jsonPropertyInfo.JsonTypeInfo.CreateObject == null)
{
// Avoid a reference to the JsonNode type for trimming
if (jsonPropertyInfo.DeclaredPropertyType.FullName == JsonTypeInfo.JsonObjectTypeName)
if (jsonPropertyInfo.PropertyType.FullName == JsonTypeInfo.JsonObjectTypeName)
{
extensionData = jsonPropertyInfo.ConverterBase.CreateObject(options);
}
else
{
ThrowHelper.ThrowNotSupportedException_SerializationNotSupported(jsonPropertyInfo.DeclaredPropertyType);
ThrowHelper.ThrowNotSupportedException_SerializationNotSupported(jsonPropertyInfo.PropertyType);
}
}
else
{
extensionData = jsonPropertyInfo.RuntimeTypeInfo.CreateObject();
extensionData = jsonPropertyInfo.JsonTypeInfo.CreateObject();
}
jsonPropertyInfo.SetExtensionDictionaryAsObject(obj, extensionData);
......
......@@ -334,7 +334,7 @@ public static partial class JsonSerializer
[UnconditionalSuppressMessage("ReflectionAnalysis", "IL2026:RequiresUnreferencedCode",
Justification = "Workaround for https://github.com/mono/linker/issues/1416. All usages are marked as unsafe.")]
private static JsonTypeInfo CreateQueueJsonTypeInfo<TValue>(JsonConverter queueConverter, JsonSerializerOptions queueOptions) =>
new JsonTypeInfo(typeof(Queue<TValue>), queueConverter, typeof(Queue<TValue>), queueOptions);
new JsonTypeInfo(typeof(Queue<TValue>), queueConverter, queueOptions);
internal static async ValueTask<TValue?> ReadAllAsync<TValue>(
Stream utf8Json,
......
......@@ -93,7 +93,7 @@ private static void RootBuiltInConverters()
/// </remarks>
public IList<JsonConverter> Converters { get; }
internal JsonConverter GetConverterFromMember(Type? parentClassType, Type runtimePropertyType, MemberInfo? memberInfo)
internal JsonConverter GetConverterFromMember(Type? parentClassType, Type propertyType, MemberInfo? memberInfo)
{
JsonConverter converter = null!;
......@@ -107,19 +107,19 @@ internal JsonConverter GetConverterFromMember(Type? parentClassType, Type runtim
if (converterAttribute != null)
{
converter = GetConverterFromAttribute(converterAttribute, typeToConvert: runtimePropertyType, classTypeAttributeIsOn: parentClassType!, memberInfo);
converter = GetConverterFromAttribute(converterAttribute, typeToConvert: propertyType, classTypeAttributeIsOn: parentClassType!, memberInfo);
}
}
if (converter == null)
{
converter = GetConverterInternal(runtimePropertyType);
converter = GetConverterInternal(propertyType);
Debug.Assert(converter != null);
}
if (converter is JsonConverterFactory factory)
{
converter = factory.GetConverterInternal(runtimePropertyType, this);
converter = factory.GetConverterInternal(propertyType, this);
// A factory cannot return null; GetConverterInternal checked for that.
Debug.Assert(converter != null);
......@@ -133,10 +133,10 @@ internal JsonConverter GetConverterFromMember(Type? parentClassType, Type runtim
//
// We also throw to avoid passing an invalid argument to setters for nullable struct properties,
// which would cause an InvalidProgramException when the generated IL is invoked.
if (runtimePropertyType.IsValueType && converter.IsValueType &&
(runtimePropertyType.IsNullableOfT() ^ converter.TypeToConvert.IsNullableOfT()))
if (propertyType.IsValueType && converter.IsValueType &&
(propertyType.IsNullableOfT() ^ converter.TypeToConvert.IsNullableOfT()))
{
ThrowHelper.ThrowInvalidOperationException_ConverterCanConvertMultipleTypes(runtimePropertyType, converter);
ThrowHelper.ThrowInvalidOperationException_ConverterCanConvertMultipleTypes(propertyType, converter);
}
return converter;
......
......@@ -82,6 +82,7 @@ public static JsonTypeInfo<T> CreateValueInfo<T>(JsonSerializerOptions options,
{
JsonTypeInfo<T> info = new JsonTypeInfoInternal<T>(options);
info.PropertyInfoForTypeInfo = CreateJsonPropertyInfoForClassInfo(typeof(T), info, converter, options);
converter.ConfigureJsonTypeInfo(info, options);
return info;
}
......
......@@ -11,7 +11,7 @@ namespace System.Text.Json.Serialization.Metadata
/// </summary>
internal abstract class JsonParameterInfo
{
private JsonTypeInfo? _runtimeTypeInfo;
private JsonTypeInfo? _jsonTypeInfo;
public JsonConverter ConverterBase { get; private set; } = null!;
......@@ -33,28 +33,23 @@ internal abstract class JsonParameterInfo
// Using a field to avoid copy semantics.
public JsonParameterInfoValues ClrInfo = null!;
public JsonTypeInfo RuntimeTypeInfo
public JsonTypeInfo JsonTypeInfo
{
get
{
Debug.Assert(Options != null);
Debug.Assert(ShouldDeserialize);
if (_runtimeTypeInfo == null)
{
Debug.Assert(Options != null);
_runtimeTypeInfo = Options!.GetOrAddJsonTypeInfo(RuntimePropertyType);
}
return _runtimeTypeInfo;
return _jsonTypeInfo ??= Options.GetOrAddJsonTypeInfo(PropertyType);
}
set
{
// Used by JsonMetadataServices.
Debug.Assert(_runtimeTypeInfo == null);
_runtimeTypeInfo = value;
Debug.Assert(_jsonTypeInfo == null);
_jsonTypeInfo = value;
}
}
public Type RuntimePropertyType { get; set; } = null!;
public Type PropertyType { get; set; } = null!;
public bool ShouldDeserialize { get; private set; }
......@@ -64,7 +59,7 @@ public virtual void Initialize(JsonParameterInfoValues parameterInfo, JsonProper
Options = options;
ShouldDeserialize = true;
RuntimePropertyType = matchingProperty.RuntimePropertyType!;
PropertyType = matchingProperty.PropertyType;
NameAsUtf8Bytes = matchingProperty.NameAsUtf8Bytes!;
ConverterBase = matchingProperty.ConverterBase;
IgnoreDefaultValuesOnRead = matchingProperty.IgnoreDefaultValuesOnRead;
......@@ -83,7 +78,7 @@ public virtual void Initialize(JsonParameterInfoValues parameterInfo, JsonProper
{
JsonParameterInfo jsonParameterInfo = new JsonParameterInfo<sbyte>();
jsonParameterInfo.ClrInfo = parameterInfo;
jsonParameterInfo.RuntimePropertyType = matchingProperty.RuntimePropertyType!;
jsonParameterInfo.PropertyType = matchingProperty.PropertyType;
jsonParameterInfo.NameAsUtf8Bytes = matchingProperty.NameAsUtf8Bytes!;
// TODO: https://github.com/dotnet/runtime/issues/60082.
......
......@@ -21,7 +21,7 @@ public override void Initialize(JsonParameterInfoValues parameterInfo, JsonPrope
private void InitializeDefaultValue(JsonPropertyInfo matchingProperty)
{
Debug.Assert(ClrInfo.ParameterType == matchingProperty.DeclaredPropertyType);
Debug.Assert(ClrInfo.ParameterType == matchingProperty.PropertyType);
if (ClrInfo.HasDefaultValue)
{
......
......@@ -17,7 +17,7 @@ public abstract class JsonPropertyInfo
{
internal static readonly JsonPropertyInfo s_missingProperty = GetPropertyPlaceholder();
private JsonTypeInfo? _runtimeTypeInfo;
private JsonTypeInfo? _jsonTypeInfo;
internal ConverterStrategy ConverterStrategy;
......@@ -52,7 +52,7 @@ internal static JsonPropertyInfo GetPropertyPlaceholder()
jsonPropertyInfo.Options = options;
jsonPropertyInfo.MemberInfo = memberInfo;
jsonPropertyInfo.IsIgnored = true;
jsonPropertyInfo.DeclaredPropertyType = memberType;
jsonPropertyInfo.PropertyType = memberType;
jsonPropertyInfo.IsVirtual = isVirtual;
jsonPropertyInfo.DeterminePropertyName();
......@@ -61,7 +61,7 @@ internal static JsonPropertyInfo GetPropertyPlaceholder()
return jsonPropertyInfo;
}
internal Type DeclaredPropertyType { get; set; } = null!;
internal Type PropertyType { get; set; } = null!;
internal virtual void GetPolicies(JsonIgnoreCondition? ignoreCondition, JsonNumberHandling? declaringTypeNumberHandling)
{
......@@ -273,7 +273,7 @@ private bool NumberHandingIsApplicable()
if (!ConverterBase.IsInternalConverter ||
((ConverterStrategy.Enumerable | ConverterStrategy.Dictionary) & ConverterStrategy) == 0)
{
potentialNumberType = DeclaredPropertyType;
potentialNumberType = PropertyType;
}
else
{
......@@ -313,8 +313,7 @@ private bool NumberHandingIsApplicable()
internal virtual void Initialize(
Type parentClassType,
Type declaredPropertyType,
Type? runtimePropertyType,
ConverterStrategy runtimeClassType,
ConverterStrategy converterStrategy,
MemberInfo? memberInfo,
bool isVirtual,
JsonConverter converter,
......@@ -325,9 +324,8 @@ private bool NumberHandingIsApplicable()
Debug.Assert(converter != null);
DeclaringType = parentClassType;
DeclaredPropertyType = declaredPropertyType;
RuntimePropertyType = runtimePropertyType;
ConverterStrategy = runtimeClassType;
PropertyType = declaredPropertyType;
ConverterStrategy = converterStrategy;
MemberInfo = memberInfo;
IsVirtual = isVirtual;
ConverterBase = converter;
......@@ -418,7 +416,7 @@ private bool NumberHandingIsApplicable()
JsonConverter GetDictionaryValueConverter(Type dictionaryValueType)
{
JsonConverter converter;
JsonTypeInfo? dictionaryValueInfo = RuntimeTypeInfo.ElementTypeInfo;
JsonTypeInfo? dictionaryValueInfo = JsonTypeInfo.ElementTypeInfo;
if (dictionaryValueInfo != null)
{
// Fast path when there is a generic type such as Dictionary<,>.
......@@ -445,7 +443,7 @@ internal bool ReadJsonExtensionDataValue(ref ReadStack state, ref Utf8JsonReader
{
Debug.Assert(this == state.Current.JsonTypeInfo.DataExtensionProperty);
if (RuntimeTypeInfo.ElementType == JsonTypeInfo.ObjectType && reader.TokenType == JsonTokenType.Null)
if (JsonTypeInfo.ElementType == JsonTypeInfo.ObjectType && reader.TokenType == JsonTokenType.Null)
{
value = null;
return true;
......@@ -467,27 +465,20 @@ internal bool ReadJsonExtensionDataValue(ref ReadStack state, ref Utf8JsonReader
internal MemberInfo? MemberInfo { get; private set; }
internal JsonTypeInfo RuntimeTypeInfo
internal JsonTypeInfo JsonTypeInfo
{
get
{
if (_runtimeTypeInfo == null)
{
_runtimeTypeInfo = Options.GetOrAddJsonTypeInfo(RuntimePropertyType!);
}
return _runtimeTypeInfo;
return _jsonTypeInfo ??= Options.GetOrAddJsonTypeInfo(PropertyType);
}
set
{
// Used by JsonMetadataServices.
Debug.Assert(_runtimeTypeInfo == null);
_runtimeTypeInfo = value;
Debug.Assert(_jsonTypeInfo == null);
_jsonTypeInfo = value;
}
}
internal Type? RuntimePropertyType { get; set; }
internal abstract void SetExtensionDictionaryAsObject(object obj, object? extensionDict);
internal bool ShouldSerialize { get; set; }
......
......@@ -38,8 +38,7 @@ internal sealed class JsonPropertyInfo<T> : JsonPropertyInfo
internal override void Initialize(
Type parentClassType,
Type declaredPropertyType,
Type? runtimePropertyType,
ConverterStrategy runtimeClassType,
ConverterStrategy converterStrategy,
MemberInfo? memberInfo,
bool isVirtual,
JsonConverter converter,
......@@ -50,8 +49,7 @@ internal sealed class JsonPropertyInfo<T> : JsonPropertyInfo
base.Initialize(
parentClassType,
declaredPropertyType,
runtimePropertyType,
runtimeClassType,
converterStrategy,
memberInfo,
isVirtual,
converter,
......@@ -112,9 +110,9 @@ internal sealed class JsonPropertyInfo<T> : JsonPropertyInfo
}
}
_converterIsExternalAndPolymorphic = !converter.IsInternalConverter && DeclaredPropertyType != converter.TypeToConvert;
PropertyTypeCanBeNull = DeclaredPropertyType.CanBeNull();
_propertyTypeEqualsTypeToConvert = typeof(T) == DeclaredPropertyType;
_converterIsExternalAndPolymorphic = !converter.IsInternalConverter && PropertyType != converter.TypeToConvert;
PropertyTypeCanBeNull = PropertyType.CanBeNull();
_propertyTypeEqualsTypeToConvert = typeof(T) == PropertyType;
GetPolicies(ignoreCondition, parentTypeNumberHandling);
}
......@@ -147,7 +145,7 @@ internal void InitializeForSourceGen(JsonSerializerOptions options, JsonProperty
SrcGen_IsPublic = propertyInfo.IsPublic;
SrcGen_HasJsonInclude = propertyInfo.HasJsonInclude;
SrcGen_IsExtensionData = propertyInfo.IsExtensionData;
DeclaredPropertyType = typeof(T);
PropertyType = typeof(T);
JsonTypeInfo propertyTypeInfo = propertyInfo.PropertyTypeInfo;
Type declaringType = propertyInfo.DeclaringType;
......@@ -176,16 +174,15 @@ internal void InitializeForSourceGen(JsonSerializerOptions options, JsonProperty
Set = propertyInfo.Setter;
HasGetter = Get != null;
HasSetter = Set != null;
RuntimeTypeInfo = propertyTypeInfo;
JsonTypeInfo = propertyTypeInfo;
DeclaringType = declaringType;
IgnoreCondition = propertyInfo.IgnoreCondition;
MemberType = propertyInfo.IsProperty ? MemberTypes.Property : MemberTypes.Field;
_converterIsExternalAndPolymorphic = !ConverterBase.IsInternalConverter && DeclaredPropertyType != ConverterBase.TypeToConvert;
_converterIsExternalAndPolymorphic = !ConverterBase.IsInternalConverter && PropertyType != ConverterBase.TypeToConvert;
PropertyTypeCanBeNull = typeof(T).CanBeNull();
_propertyTypeEqualsTypeToConvert = ConverterBase.TypeToConvert == typeof(T);
ConverterStrategy = Converter!.ConverterStrategy;
RuntimePropertyType = DeclaredPropertyType;
DetermineIgnoreCondition(IgnoreCondition);
// TODO: this method needs to also take the number handling option for the declaring type.
DetermineNumberHandlingForProperty(propertyInfo.NumberHandling, declaringTypeNumberHandling: null);
......@@ -203,10 +200,9 @@ internal void InitializeForSourceGen(JsonSerializerOptions options, JsonProperty
JsonConverter converter,
JsonSerializerOptions options)
{
DeclaredPropertyType = declaredType;
RuntimePropertyType = declaredType;
PropertyType = declaredType;
ConverterStrategy = converter.ConverterStrategy;
RuntimeTypeInfo = runtimeTypeInfo;
JsonTypeInfo = runtimeTypeInfo;
ConverterBase = converter;
Options = options;
IsForTypeInfo = true;
......@@ -284,10 +280,10 @@ internal override bool GetMemberAndWriteJson(object obj, ref WriteStack state, U
}
else
{
Debug.Assert(RuntimeTypeInfo.Type == DeclaredPropertyType);
Debug.Assert(JsonTypeInfo.Type == PropertyType);
// Use a late-bound call to EqualityComparer<DeclaredPropertyType>.
if (RuntimeTypeInfo.DefaultValueHolder.IsDefaultValue(value))
if (JsonTypeInfo.DefaultValueHolder.IsDefaultValue(value))
{
return true;
}
......@@ -380,7 +376,7 @@ internal override bool ReadJsonAndSetMember(object obj, ref ReadStack state, ref
if (!isNullToken || !IgnoreDefaultValuesOnRead || !PropertyTypeCanBeNull)
{
// Optimize for internal converters by avoiding the extra call to TryRead.
T? fastValue = Converter.Read(ref reader, RuntimePropertyType!, Options);
T? fastValue = Converter.Read(ref reader, PropertyType, Options);
Set!(obj, fastValue!);
}
......@@ -391,7 +387,7 @@ internal override bool ReadJsonAndSetMember(object obj, ref ReadStack state, ref
success = true;
if (!isNullToken || !IgnoreDefaultValuesOnRead || !PropertyTypeCanBeNull || state.IsContinuation)
{
success = Converter.TryRead(ref reader, RuntimePropertyType!, Options, ref state, out T? value);
success = Converter.TryRead(ref reader, PropertyType, Options, ref state, out T? value);
if (success)
{
#if !DEBUG
......@@ -401,14 +397,14 @@ internal override bool ReadJsonAndSetMember(object obj, ref ReadStack state, ref
if (value != null)
{
Type typeOfValue = value.GetType();
if (!DeclaredPropertyType.IsAssignableFrom(typeOfValue))
if (!PropertyType.IsAssignableFrom(typeOfValue))
{
ThrowHelper.ThrowInvalidCastException_DeserializeUnableToAssignValue(typeOfValue, DeclaredPropertyType);
ThrowHelper.ThrowInvalidCastException_DeserializeUnableToAssignValue(typeOfValue, PropertyType);
}
}
else if (!PropertyTypeCanBeNull)
{
ThrowHelper.ThrowInvalidOperationException_DeserializeUnableToAssignNull(DeclaredPropertyType);
ThrowHelper.ThrowInvalidOperationException_DeserializeUnableToAssignNull(PropertyType);
}
}
......@@ -442,12 +438,12 @@ internal override bool ReadJsonAsObject(ref ReadStack state, ref Utf8JsonReader
// CanUseDirectReadOrWrite == false when using streams
Debug.Assert(!state.IsContinuation);
value = Converter.Read(ref reader, RuntimePropertyType!, Options);
value = Converter.Read(ref reader, PropertyType, Options);
success = true;
}
else
{
success = Converter.TryRead(ref reader, RuntimePropertyType!, Options, ref state, out T? typedValue);
success = Converter.TryRead(ref reader, PropertyType, Options, ref state, out T? typedValue);
value = typedValue;
}
}
......
......@@ -68,12 +68,10 @@ public partial class JsonTypeInfo
memberType,
parentClassType,
memberInfo,
out Type runtimeType,
options);
return CreateProperty(
declaredPropertyType: memberType,
runtimePropertyType: runtimeType,
memberInfo,
parentClassType,
isVirtual,
......@@ -85,7 +83,6 @@ public partial class JsonTypeInfo
internal static JsonPropertyInfo CreateProperty(
Type declaredPropertyType,
Type? runtimePropertyType,
MemberInfo? memberInfo,
Type parentClassType,
bool isVirtual,
......@@ -100,8 +97,7 @@ public partial class JsonTypeInfo
jsonPropertyInfo.Initialize(
parentClassType,
declaredPropertyType,
runtimePropertyType,
runtimeClassType: converter.ConverterStrategy,
converterStrategy: converter.ConverterStrategy,
memberInfo,
isVirtual,
converter,
......@@ -118,14 +114,12 @@ public partial class JsonTypeInfo
/// </summary>
internal static JsonPropertyInfo CreatePropertyInfoForTypeInfo(
Type declaredPropertyType,
Type runtimePropertyType,
JsonConverter converter,
JsonNumberHandling? numberHandling,
JsonSerializerOptions options)
{
JsonPropertyInfo jsonPropertyInfo = CreateProperty(
declaredPropertyType: declaredPropertyType,
runtimePropertyType: runtimePropertyType,
memberInfo: null, // Not a real property so this is null.
parentClassType: ObjectType, // a dummy value (not used)
isVirtual: false,
......
......@@ -154,22 +154,20 @@ internal JsonTypeInfo(Type type, JsonSerializerOptions options!!, bool dummy)
type,
parentClassType: null, // A TypeInfo never has a "parent" class.
memberInfo: null, // A TypeInfo never has a "parent" property.
out Type runtimeType,
options),
runtimeType,
options)
{
}
[RequiresUnreferencedCode(JsonSerializer.SerializationUnreferencedCodeMessage)]
internal JsonTypeInfo(Type type, JsonConverter converter, Type runtimeType, JsonSerializerOptions options)
internal JsonTypeInfo(Type type, JsonConverter converter, JsonSerializerOptions options)
{
Type = type;
Options = options;
JsonNumberHandling? typeNumberHandling = GetNumberHandlingForType(Type);
PropertyInfoForTypeInfo = CreatePropertyInfoForTypeInfo(Type, runtimeType, converter, typeNumberHandling, Options);
PropertyInfoForTypeInfo = CreatePropertyInfoForTypeInfo(Type, converter, typeNumberHandling, Options);
ElementType = converter.ElementType;
......@@ -289,11 +287,6 @@ internal JsonTypeInfo(Type type, JsonConverter converter, Type runtimeType, Json
if (converter.ConstructorIsParameterized)
{
// Create dynamic accessor to parameterized ctor.
// Only applies to the converter that handles "large ctors",
// since it is the only one shared with the source-gen code-paths.
converter.Initialize(Options, this);
ParameterInfo[] parameters = converter.ConstructorInfo!.GetParameters();
int parameterCount = parameters.Length;
......@@ -304,23 +297,13 @@ internal JsonTypeInfo(Type type, JsonConverter converter, Type runtimeType, Json
break;
case ConverterStrategy.Enumerable:
{
CreateObject = Options.MemberAccessorStrategy.CreateConstructor(runtimeType);
if (converter.RequiresDynamicMemberAccessors)
{
converter.Initialize(Options, this);
}
CreateObject = Options.MemberAccessorStrategy.CreateConstructor(type);
}
break;
case ConverterStrategy.Dictionary:
{
KeyType = converter.KeyType;
CreateObject = Options.MemberAccessorStrategy.CreateConstructor(runtimeType);
if (converter.RequiresDynamicMemberAccessors)
{
converter.Initialize(Options, this);
}
CreateObject = Options.MemberAccessorStrategy.CreateConstructor(type);
}
break;
case ConverterStrategy.Value:
......@@ -337,6 +320,11 @@ internal JsonTypeInfo(Type type, JsonConverter converter, Type runtimeType, Json
Debug.Fail($"Unexpected class type: {PropertyInfoForTypeInfo.ConverterStrategy}");
throw new InvalidOperationException();
}
// These two method overrides are expected to perform
// orthogonal changes, so we can invoke them both safely.
converter.ConfigureJsonTypeInfo(this, options);
converter.ConfigureJsonTypeInfoUsingReflection(this, options);
}
private void CacheMember(
......@@ -458,7 +446,7 @@ private void InitializeConstructorParameters(JsonParameterInfoValues[] jsonParam
JsonPropertyInfo jsonProperty = kvp.Value!;
string propertyName = jsonProperty.ClrName!;
ParameterLookupKey key = new(propertyName, jsonProperty.DeclaredPropertyType);
ParameterLookupKey key = new(propertyName, jsonProperty.PropertyType);
ParameterLookupValue value = new(jsonProperty);
if (!JsonHelpers.TryAdd(nameLookup, key, value))
......@@ -538,7 +526,7 @@ private static JsonParameterInfoValues[] GetParameterInfoArray(ParameterInfo[] p
return false;
}
return currentMemberType == ignoredMember.DeclaredPropertyType &&
return currentMemberType == ignoredMember.PropertyType &&
currentMemberIsVirtual &&
ignoredMember.IsVirtual;
}
......@@ -555,7 +543,7 @@ private void ValidateAndAssignDataExtensionProperty(JsonPropertyInfo jsonPropert
private bool IsValidDataExtensionProperty(JsonPropertyInfo jsonPropertyInfo)
{
Type memberType = jsonPropertyInfo.DeclaredPropertyType;
Type memberType = jsonPropertyInfo.PropertyType;
bool typeIsValid = typeof(IDictionary<string, object>).IsAssignableFrom(memberType) ||
typeof(IDictionary<string, JsonElement>).IsAssignableFrom(memberType) ||
......@@ -587,64 +575,17 @@ private bool IsValidDataExtensionProperty(JsonPropertyInfo jsonPropertyInfo)
// This method gets the runtime information for a given type or property.
// The runtime information consists of the following:
// - class type,
// - runtime type,
// - element type (if the type is a collection),
// - the converter (either native or custom), if one exists.
private static JsonConverter GetConverter(
Type type,
Type? parentClassType,
MemberInfo? memberInfo,
out Type runtimeType,
JsonSerializerOptions options)
{
Debug.Assert(type != null);
ValidateType(type, parentClassType, memberInfo, options);
JsonConverter converter = options.GetConverterFromMember(parentClassType, type, memberInfo);
// The runtimeType is the actual value being assigned to the property.
// There are three types to consider for the runtimeType:
// 1) The declared type (the actual property type).
// 2) The converter.TypeToConvert (the T value that the converter supports).
// 3) The converter.RuntimeType (used with interfaces such as IList).
Type converterRuntimeType = converter.RuntimeType;
if (type == converterRuntimeType)
{
runtimeType = type;
}
else
{
if (type.IsInterface)
{
runtimeType = converterRuntimeType;
}
else if (converterRuntimeType.IsInterface)
{
runtimeType = type;
}
else
{
// Use the most derived version from the converter.RuntimeType or converter.TypeToConvert.
if (type.IsAssignableFrom(converterRuntimeType))
{
runtimeType = converterRuntimeType;
}
else if (converterRuntimeType.IsAssignableFrom(type) || converter.TypeToConvert.IsAssignableFrom(type))
{
runtimeType = type;
}
else
{
runtimeType = default!;
ThrowHelper.ThrowNotSupportedException_SerializationNotSupported(type);
}
}
}
Debug.Assert(!IsInvalidForSerialization(runtimeType));
return converter;
return options.GetConverterFromMember(parentClassType, type, memberInfo);
}
private static void ValidateType(Type type, Type? parentClassType, MemberInfo? memberInfo, JsonSerializerOptions options)
......
......@@ -48,6 +48,7 @@ public JsonTypeInfoInternal(JsonSerializerOptions options, JsonObjectInfoValues<
SerializeHandler = objectInfo.SerializeHandler;
PropertyInfoForTypeInfo = JsonMetadataServices.CreateJsonPropertyInfoForClassInfo(typeof(T), this, converter, Options);
NumberHandling = objectInfo.NumberHandling;
converter.ConfigureJsonTypeInfo(this, Options);
}
/// <summary>
......@@ -74,6 +75,7 @@ public JsonTypeInfoInternal(JsonSerializerOptions options, JsonObjectInfoValues<
CreateObjectWithArgs = createObjectWithArgs;
AddMethodDelegate = addFunc;
SetCreateObjectFunc(collectionInfo.ObjectCreator);
converter.ConfigureJsonTypeInfo(this, Options);
}
private void SetCreateObjectFunc(Func<T>? createObjectFunc)
......
......@@ -126,17 +126,17 @@ public void Push()
{
if (Current.JsonPropertyInfo != null)
{
jsonTypeInfo = Current.JsonPropertyInfo.RuntimeTypeInfo;
jsonTypeInfo = Current.JsonPropertyInfo.JsonTypeInfo;
}
else
{
jsonTypeInfo = Current.CtorArgumentState!.JsonParameterInfo!.RuntimeTypeInfo;
jsonTypeInfo = Current.CtorArgumentState!.JsonParameterInfo!.JsonTypeInfo;
}
}
else if (converterStrategy == ConverterStrategy.Value)
{
// Although ConverterStrategy.Value doesn't push, a custom custom converter may re-enter serialization.
jsonTypeInfo = Current.JsonPropertyInfo!.RuntimeTypeInfo;
jsonTypeInfo = Current.JsonPropertyInfo!.JsonTypeInfo;
}
else
{
......
......@@ -132,7 +132,7 @@ public void Push()
}
else
{
JsonTypeInfo jsonTypeInfo = Current.GetPolymorphicJsonPropertyInfo().RuntimeTypeInfo;
JsonTypeInfo jsonTypeInfo = Current.GetPolymorphicJsonPropertyInfo().JsonTypeInfo;
JsonNumberHandling? numberHandling = Current.NumberHandling;
EnsurePushCapacity();
......
......@@ -112,7 +112,7 @@ public JsonConverter InitializeReEntry(Type type, JsonSerializerOptions options)
{
// For perf, avoid the dictionary lookup in GetOrAddClass() for every element of a collection
// if the current element is the same type as the previous element.
if (PolymorphicJsonPropertyInfo?.RuntimePropertyType != type)
if (PolymorphicJsonPropertyInfo?.PropertyType != type)
{
JsonTypeInfo typeInfo = options.GetOrAddJsonTypeInfo(type);
PolymorphicJsonPropertyInfo = typeInfo.PropertyInfoForTypeInfo;
......
......@@ -290,7 +290,7 @@ public static void AddJsonExceptionInformation(ref ReadStack state, in Utf8JsonR
if (string.IsNullOrEmpty(message))
{
// Use a default message.
Type? propertyType = state.Current.JsonPropertyInfo?.RuntimePropertyType;
Type? propertyType = state.Current.JsonPropertyInfo?.PropertyType;
if (propertyType == null)
{
propertyType = state.Current.JsonTypeInfo?.Type;
......@@ -376,7 +376,7 @@ public static void ThrowNotSupportedException(ref ReadStack state, in Utf8JsonRe
Debug.Assert(!message.Contains(" Path: "));
// Obtain the type to show in the message.
Type? propertyType = state.Current.JsonPropertyInfo?.RuntimePropertyType;
Type? propertyType = state.Current.JsonPropertyInfo?.PropertyType;
if (propertyType == null)
{
propertyType = state.Current.JsonTypeInfo.Type;
......@@ -408,7 +408,7 @@ public static void ThrowNotSupportedException(ref WriteStack state, NotSupported
Debug.Assert(!message.Contains(" Path: "));
// Obtain the type to show in the message.
Type? propertyType = state.Current.DeclaredJsonPropertyInfo?.RuntimePropertyType;
Type? propertyType = state.Current.DeclaredJsonPropertyInfo?.PropertyType;
if (propertyType == null)
{
propertyType = state.Current.JsonTypeInfo.Type;
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册