| File: System\Text\Json\Serialization\JsonSerializer.Helpers.cs | Web Access |
| Project: src\runtime\src\libraries\System.Text.Json\src\System.Text.Json.csproj (System.Text.Json) |
// Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. using System.Collections.Generic; using System.Diagnostics; using System.Diagnostics.CodeAnalysis; using System.Text.Json.Serialization; using System.Text.Json.Serialization.Converters; using System.Text.Json.Serialization.Metadata; namespace System.Text.Json { public static partial class JsonSerializer { internal const string SerializationUnreferencedCodeMessage = "JSON serialization and deserialization might require types that cannot be statically analyzed. Use the overload that takes a JsonTypeInfo or JsonSerializerContext, or make sure all of the required types are preserved."; internal const string SerializationRequiresDynamicCodeMessage = "JSON serialization and deserialization might require types that cannot be statically analyzed and might need runtime code generation. Use System.Text.Json source generation for native AOT applications."; /// <summary> /// Indicates whether unconfigured <see cref="JsonSerializerOptions"/> instances /// should be set to use the reflection-based <see cref="DefaultJsonTypeInfoResolver"/>. /// </summary> /// <remarks> /// The value of the property is backed by the "System.Text.Json.JsonSerializer.IsReflectionEnabledByDefault" /// <see cref="AppContext"/> setting and defaults to <see langword="true"/> if unset. /// </remarks> [FeatureSwitchDefinition("System.Text.Json.JsonSerializer.IsReflectionEnabledByDefault")] public static bool IsReflectionEnabledByDefault { get; } = AppContext.TryGetSwitch( switchName: "System.Text.Json.JsonSerializer.IsReflectionEnabledByDefault", isEnabled: out bool value) ? value : true; [RequiresUnreferencedCode(SerializationUnreferencedCodeMessage)] [RequiresDynamicCode(SerializationRequiresDynamicCodeMessage)] private static JsonTypeInfo GetTypeInfo(JsonSerializerOptions? options, Type inputType) { Debug.Assert(inputType != null); options ??= JsonSerializerOptions.Default; options.MakeReadOnly(populateMissingResolver: true); // In order to improve performance of polymorphic root-level object serialization, // we bypass GetTypeInfoForRootType and cache JsonTypeInfo<object> in a dedicated property. // This lets any derived types take advantage of the cache in GetTypeInfoForRootType themselves. return inputType == JsonTypeInfo.ObjectType ? options.ObjectTypeInfo : options.GetTypeInfoForRootType(inputType); } [RequiresUnreferencedCode(SerializationUnreferencedCodeMessage)] [RequiresDynamicCode(SerializationRequiresDynamicCodeMessage)] private static JsonTypeInfo<T> GetTypeInfo<T>(JsonSerializerOptions? options) => (JsonTypeInfo<T>)GetTypeInfo(options, typeof(T)); private static JsonTypeInfo GetTypeInfo(JsonSerializerContext context, Type inputType) { Debug.Assert(context is not null); Debug.Assert(inputType != null); JsonTypeInfo? info = context.GetTypeInfo(inputType); if (info is null) { ThrowHelper.ThrowInvalidOperationException_NoMetadataForType(inputType, context); } info.EnsureConfigured(); return info; } private static void ValidateInputType(object? value, Type inputType) { ArgumentNullException.ThrowIfNull(inputType); if (value is not null) { Type runtimeType = value.GetType(); if (!inputType.IsAssignableFrom(runtimeType)) { ThrowHelper.ThrowArgumentException_DeserializeWrongType(inputType, value); } } } internal static bool IsValidNumberHandlingValue(JsonNumberHandling handling) => JsonHelpers.IsInRangeInclusive((int)handling, 0, (int)( JsonNumberHandling.Strict | JsonNumberHandling.AllowReadingFromString | JsonNumberHandling.WriteAsString | JsonNumberHandling.AllowNamedFloatingPointLiterals)); internal static bool IsValidCreationHandlingValue(JsonObjectCreationHandling handling) => handling is JsonObjectCreationHandling.Replace or JsonObjectCreationHandling.Populate; internal static bool IsValidUnmappedMemberHandlingValue(JsonUnmappedMemberHandling handling) => handling is JsonUnmappedMemberHandling.Skip or JsonUnmappedMemberHandling.Disallow; [return: NotNullIfNotNull(nameof(value))] internal static T? UnboxOnRead<T>(object? value) { if (value is null) { if (default(T) is not null) { // Casting null values to a non-nullable struct throws NullReferenceException. ThrowUnableToCastValue(value); } return default; } if (value is T typedValue) { return typedValue; } ThrowUnableToCastValue(value); return default!; static void ThrowUnableToCastValue(object? value) { if (value is null) { ThrowHelper.ThrowInvalidOperationException_DeserializeUnableToAssignNull(declaredType: typeof(T)); } else { ThrowHelper.ThrowInvalidCastException_DeserializeUnableToAssignValue(typeOfValue: value.GetType(), declaredType: typeof(T)); } } } [return: NotNullIfNotNull(nameof(value))] internal static T? UnboxOnWrite<T>(object? value) { if (default(T) is not null && value is null) { // Casting null values to a non-nullable struct throws NullReferenceException. ThrowHelper.ThrowJsonException_DeserializeUnableToConvertValue(typeof(T)); } return (T?)value; } private static JsonTypeInfo<List<T?>> GetOrAddListTypeInfoForRootLevelValueMode<T>(JsonTypeInfo<T> elementTypeInfo) { if (elementTypeInfo._asyncEnumerableRootLevelValueTypeInfo is not null) { return (JsonTypeInfo<List<T?>>)elementTypeInfo._asyncEnumerableRootLevelValueTypeInfo; } var converter = new RootLevelListConverter<T>(elementTypeInfo); var listTypeInfo = new JsonTypeInfo<List<T?>>(converter, elementTypeInfo.Options) { ElementTypeInfo = elementTypeInfo, }; listTypeInfo.EnsureConfigured(); elementTypeInfo._asyncEnumerableRootLevelValueTypeInfo = listTypeInfo; return listTypeInfo; } private static JsonTypeInfo<List<T?>> GetOrAddListTypeInfoForArrayMode<T>(JsonTypeInfo<T> elementTypeInfo) { if (elementTypeInfo._asyncEnumerableArrayTypeInfo is not null) { return (JsonTypeInfo<List<T?>>)elementTypeInfo._asyncEnumerableArrayTypeInfo; } var converter = new ListOfTConverter<List<T>, T>(); var listTypeInfo = new JsonTypeInfo<List<T?>>(converter, elementTypeInfo.Options) { CreateObject = static () => new List<T?>(), ElementTypeInfo = elementTypeInfo, }; listTypeInfo.EnsureConfigured(); elementTypeInfo._asyncEnumerableArrayTypeInfo = listTypeInfo; return listTypeInfo; } private static JsonTypeInfo<IAsyncEnumerable<T>> GetOrAddIAsyncEnumerableTypeInfoForSerialize<T>(JsonTypeInfo<T> elementTypeInfo) { if (elementTypeInfo._asyncEnumerableRootLevelSerializer is not null) { return (JsonTypeInfo<IAsyncEnumerable<T>>)elementTypeInfo._asyncEnumerableRootLevelSerializer; } // Synthesize the IAsyncEnumerable<T> type info from the element type info so we don't depend on the // resolver (e.g. a source-generated context) having registered IAsyncEnumerable<T> explicitly. var converter = new IAsyncEnumerableOfTConverter<IAsyncEnumerable<T>, T>(); var asyncEnumerableTypeInfo = new JsonTypeInfo<IAsyncEnumerable<T>>(converter, elementTypeInfo.Options) { ElementTypeInfo = elementTypeInfo, }; asyncEnumerableTypeInfo.EnsureConfigured(); elementTypeInfo._asyncEnumerableRootLevelSerializer = asyncEnumerableTypeInfo; return asyncEnumerableTypeInfo; } } }