| File: System\Text\Json\Serialization\Metadata\DefaultJsonTypeInfoResolver.Converters.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.Reflection; using System.Text.Json.Reflection; using System.Text.Json.Serialization.Converters; namespace System.Text.Json.Serialization.Metadata { public partial class DefaultJsonTypeInfoResolver { private static Dictionary<Type, JsonConverter>? s_defaultSimpleConverters; private static JsonConverterFactory[]? s_defaultFactoryConverters; [RequiresUnreferencedCode(JsonSerializer.SerializationUnreferencedCodeMessage)] [RequiresDynamicCode(JsonSerializer.SerializationRequiresDynamicCodeMessage)] private static JsonConverterFactory[] GetDefaultFactoryConverters() { return [ // Check for disallowed types. new UnsupportedTypeConverterFactory(), // Nullable converter should always be next since it forwards to any nullable type. new NullableConverterFactory(), new EnumConverterFactory(), new JsonNodeConverterFactory(), new FSharpTypeConverterFactory(), new MemoryConverterFactory(), // IAsyncEnumerable takes precedence over IEnumerable. new IAsyncEnumerableConverterFactory(), // IEnumerable should always be second to last since they can convert any IEnumerable. new IEnumerableConverterFactory(), // Union converter should be before Object since it converts [JsonUnion] types. new JsonUnionConverterFactory(), // Object should always be last since it converts any type. new ObjectConverterFactory() ]; } private static Dictionary<Type, JsonConverter> GetDefaultSimpleConverters() { const int NumberOfSimpleConverters = 31; var converters = new Dictionary<Type, JsonConverter>(NumberOfSimpleConverters); // Use a dictionary for simple converters. // When adding to this, update NumberOfSimpleConverters above. // // When adding/removing a built-in converter here, also update // gen/JsonSourceGenerator.Parser.cs::GetSupportedJsonValueTypes so the union // ambiguity diagnostic (SYSLIB1227) agrees with JsonTypeInfo.BuildUnionValueTypeMap. Add(JsonMetadataServices.BooleanConverter); Add(JsonMetadataServices.ByteConverter); Add(JsonMetadataServices.ByteArrayConverter); Add(JsonMetadataServices.CharConverter); Add(JsonMetadataServices.DateTimeConverter); Add(JsonMetadataServices.DateTimeOffsetConverter); #if NET Add(JsonMetadataServices.DateOnlyConverter); Add(JsonMetadataServices.TimeOnlyConverter); Add(JsonMetadataServices.HalfConverter); #endif Add(JsonMetadataServices.DoubleConverter); Add(JsonMetadataServices.DecimalConverter); Add(JsonMetadataServices.GuidConverter); Add(JsonMetadataServices.Int16Converter); Add(JsonMetadataServices.Int32Converter); Add(JsonMetadataServices.Int64Converter); Add(JsonMetadataServices.JsonElementConverter); Add(JsonMetadataServices.JsonDocumentConverter); Add(JsonMetadataServices.MemoryByteConverter); Add(JsonMetadataServices.ReadOnlyMemoryByteConverter); Add(JsonMetadataServices.ObjectConverter); Add(JsonMetadataServices.SByteConverter); Add(JsonMetadataServices.SingleConverter); Add(JsonMetadataServices.StringConverter); Add(JsonMetadataServices.TimeSpanConverter); Add(JsonMetadataServices.UInt16Converter); Add(JsonMetadataServices.UInt32Converter); Add(JsonMetadataServices.UInt64Converter); #if NET Add(JsonMetadataServices.Int128Converter); Add(JsonMetadataServices.UInt128Converter); #endif Add(JsonMetadataServices.UriConverter); Add(JsonMetadataServices.VersionConverter); Debug.Assert(converters.Count <= NumberOfSimpleConverters); return converters; void Add(JsonConverter converter) => converters.Add(converter.Type!, converter); } [RequiresUnreferencedCode(JsonSerializer.SerializationUnreferencedCodeMessage)] [RequiresDynamicCode(JsonSerializer.SerializationRequiresDynamicCodeMessage)] private static JsonConverter GetBuiltInConverter(Type typeToConvert) { s_defaultSimpleConverters ??= GetDefaultSimpleConverters(); s_defaultFactoryConverters ??= GetDefaultFactoryConverters(); if (s_defaultSimpleConverters.TryGetValue(typeToConvert, out JsonConverter? converter)) { return converter; } else { foreach (JsonConverterFactory factory in s_defaultFactoryConverters) { if (factory.CanConvert(typeToConvert)) { converter = factory; break; } } // Since the object and IEnumerable converters cover all types, we should have a converter. Debug.Assert(converter != null); return converter; } } internal static bool TryGetDefaultSimpleConverter(Type typeToConvert, [NotNullWhen(true)] out JsonConverter? converter) { if (s_defaultSimpleConverters is null) { converter = null; return false; } return s_defaultSimpleConverters.TryGetValue(typeToConvert, out converter); } [RequiresUnreferencedCode(JsonSerializer.SerializationUnreferencedCodeMessage)] [RequiresDynamicCode(JsonSerializer.SerializationRequiresDynamicCodeMessage)] private static JsonConverter? GetCustomConverterForMember(Type typeToConvert, MemberInfo memberInfo, JsonSerializerOptions options) { Debug.Assert(memberInfo is FieldInfo or PropertyInfo); Debug.Assert(typeToConvert != null); JsonConverterAttribute? converterAttribute = memberInfo.GetUniqueCustomAttribute<JsonConverterAttribute>(inherit: false); return converterAttribute is null ? null : GetConverterFromAttribute(converterAttribute, typeToConvert, memberInfo, options); } [RequiresUnreferencedCode(JsonSerializer.SerializationUnreferencedCodeMessage)] [RequiresDynamicCode(JsonSerializer.SerializationRequiresDynamicCodeMessage)] internal static JsonConverter GetConverterForType(Type typeToConvert, JsonSerializerOptions options, bool resolveJsonConverterAttribute = true) { // Priority 1: Attempt to get custom converter from the Converters list. JsonConverter? converter = options.GetConverterFromList(typeToConvert); // Priority 2: Attempt to get converter from [JsonConverter] on the type being converted. if (resolveJsonConverterAttribute && converter == null) { JsonConverterAttribute? converterAttribute = typeToConvert.GetUniqueCustomAttribute<JsonConverterAttribute>(inherit: false); if (converterAttribute != null) { converter = GetConverterFromAttribute(converterAttribute, typeToConvert: typeToConvert, memberInfo: null, options); } } // Priority 3: Query the built-in converters. converter ??= GetBuiltInConverter(typeToConvert); // Expand if factory converter & validate. converter = options.ExpandConverterFactory(converter, typeToConvert); if (!converter.Type!.IsInSubtypeRelationshipWith(typeToConvert)) { ThrowHelper.ThrowInvalidOperationException_SerializationConverterNotCompatible(converter.GetType(), typeToConvert); } JsonSerializerOptions.CheckConverterNullabilityIsSameAsPropertyType(converter, typeToConvert); return converter; } [RequiresUnreferencedCode(JsonSerializer.SerializationUnreferencedCodeMessage)] [RequiresDynamicCode(JsonSerializer.SerializationRequiresDynamicCodeMessage)] private static JsonConverter GetConverterFromAttribute(JsonConverterAttribute converterAttribute, Type typeToConvert, MemberInfo? memberInfo, JsonSerializerOptions options) { JsonConverter? converter; Type declaringType = memberInfo?.DeclaringType ?? typeToConvert; Type? converterType = converterAttribute.ConverterType; if (converterType == null) { // Allow the attribute to create the converter. converter = converterAttribute.CreateConverter(typeToConvert); if (converter == null) { ThrowHelper.ThrowInvalidOperationException_SerializationConverterOnAttributeNotCompatible(declaringType, memberInfo, typeToConvert); } } else { // Handle open generic converter types (e.g., OptionConverter<> on Option<T>). // If the converter type is an open generic and the type to convert is a closed generic // with matching type arity, construct the closed converter type. if (converterType.IsGenericTypeDefinition) { if (typeToConvert.IsGenericType && converterType.GetGenericArguments().Length == typeToConvert.GetGenericArguments().Length) { converterType = converterType.MakeGenericType(typeToConvert.GetGenericArguments()); } else { ThrowHelper.ThrowInvalidOperationException_SerializationConverterOnAttributeOpenGenericNotCompatible(declaringType, memberInfo, converterType); } } ConstructorInfo? ctor = converterType.GetConstructor(Type.EmptyTypes); if (!typeof(JsonConverter).IsAssignableFrom(converterType) || ctor == null || !ctor.IsPublic) { ThrowHelper.ThrowInvalidOperationException_SerializationConverterOnAttributeInvalid(declaringType, memberInfo); } converter = (JsonConverter)Activator.CreateInstance(converterType)!; } Debug.Assert(converter != null); if (!converter.CanConvert(typeToConvert)) { Type? underlyingType = Nullable.GetUnderlyingType(typeToConvert); if (underlyingType != null && converter.CanConvert(underlyingType)) { if (converter is JsonConverterFactory converterFactory) { converter = converterFactory.GetConverterInternal(underlyingType, options); } // Allow nullable handling to forward to the underlying type's converter. return NullableConverterFactory.CreateValueConverter(underlyingType, converter); } ThrowHelper.ThrowInvalidOperationException_SerializationConverterOnAttributeNotCompatible(declaringType, memberInfo, typeToConvert); } return converter; } } }