| File: System\Text\Json\Serialization\Metadata\JsonMetadataServices.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.Diagnostics; using System.Reflection; using System.Text.Json.Serialization.Converters; namespace System.Text.Json.Serialization.Metadata { public static partial class JsonMetadataServices { /// <summary> /// Creates serialization metadata for a type using a simple converter. /// </summary> private static JsonTypeInfo<T> CreateCore<T>(JsonConverter converter, JsonSerializerOptions options) { var typeInfo = new JsonTypeInfo<T>(converter, options); PopulatePolymorphismMetadata(typeInfo, polymorphismOptions: null, typeClassifierFactory: null); typeInfo.MapInterfaceTypesToCallbacks(); // Plug in any converter configuration -- should be run last. converter.ConfigureJsonTypeInfo(typeInfo, options); typeInfo.IsCustomized = false; return typeInfo; } /// <summary> /// Creates serialization metadata for an object. /// </summary> private static JsonTypeInfo<T> CreateCore<T>(JsonSerializerOptions options, JsonObjectInfoValues<T> objectInfo) { JsonConverter<T> converter = GetConverter(objectInfo); var typeInfo = new JsonTypeInfo<T>(converter, options); if (objectInfo.ObjectWithParameterizedConstructorCreator is not null) { // NB parameter metadata must be populated *before* property metadata // so that properties can be linked to their associated parameters. typeInfo.CreateObjectWithArgs = objectInfo.ObjectWithParameterizedConstructorCreator; PopulateParameterInfoValues(typeInfo, objectInfo.ConstructorParameterMetadataInitializer); } else { typeInfo.SetCreateObjectIfCompatible(objectInfo.ObjectCreator); typeInfo.CreateObjectForExtensionDataProperty = ((JsonTypeInfo)typeInfo).CreateObject; } if (objectInfo.PropertyMetadataInitializer is not null) { typeInfo.SourceGenDelayedPropertyInitializer = objectInfo.PropertyMetadataInitializer; } else { typeInfo.PropertyMetadataSerializationNotSupported = true; } typeInfo.ConstructorAttributeProviderFactory = objectInfo.ConstructorAttributeProviderFactory; typeInfo.SerializeHandler = objectInfo.SerializeHandler; typeInfo.NumberHandling = objectInfo.NumberHandling; PopulatePolymorphismMetadata(typeInfo, objectInfo.PolymorphismOptions, objectInfo.TypeClassifierFactory); typeInfo.MapInterfaceTypesToCallbacks(); // Plug in any converter configuration -- should be run last. converter.ConfigureJsonTypeInfo(typeInfo, options); typeInfo.IsCustomized = false; return typeInfo; } /// <summary> /// Creates serialization metadata for a collection. /// </summary> private static JsonTypeInfo<T> CreateCore<T>( JsonSerializerOptions options, JsonCollectionInfoValues<T> collectionInfo, JsonConverter<T> converter, object? createObjectWithArgs = null, object? addFunc = null) { ArgumentNullException.ThrowIfNull(collectionInfo); converter = collectionInfo.SerializeHandler is not null ? new JsonMetadataServicesConverter<T>(converter) : converter; JsonTypeInfo<T> typeInfo = new JsonTypeInfo<T>(converter, options); typeInfo.KeyTypeInfo = collectionInfo.KeyInfo; typeInfo.ElementTypeInfo = collectionInfo.ElementInfo; Debug.Assert(typeInfo.Kind != JsonTypeInfoKind.None); typeInfo.NumberHandling = collectionInfo.NumberHandling; typeInfo.SerializeHandler = collectionInfo.SerializeHandler; typeInfo.CreateObjectWithArgs = createObjectWithArgs; typeInfo.AddMethodDelegate = addFunc; typeInfo.SetCreateObjectIfCompatible(collectionInfo.ObjectCreator); PopulatePolymorphismMetadata(typeInfo, collectionInfo.PolymorphismOptions, collectionInfo.TypeClassifierFactory); typeInfo.MapInterfaceTypesToCallbacks(); // Plug in any converter configuration -- should be run last. converter.ConfigureJsonTypeInfo(typeInfo, options); typeInfo.IsCustomized = false; return typeInfo; } private static JsonConverter<T> GetConverter<T>(JsonObjectInfoValues<T> objectInfo) { #pragma warning disable CS8714 // Nullability of type argument 'T' doesn't match 'notnull' constraint. JsonConverter<T> converter = objectInfo.ObjectWithParameterizedConstructorCreator is not null ? new LargeObjectWithParameterizedConstructorConverter<T>() : new ObjectDefaultConverter<T>(); #pragma warning restore CS8714 return objectInfo.SerializeHandler is not null ? new JsonMetadataServicesConverter<T>(converter) : converter; } private static void PopulatePolymorphismMetadata( JsonTypeInfo typeInfo, JsonPolymorphismOptions? polymorphismOptions, JsonTypeClassifierFactory? typeClassifierFactory) { if (polymorphismOptions is null) { // Older versions of the source generator do not populate polymorphism options, // so attempt to populate it from attributes at runtime. polymorphismOptions = JsonPolymorphismOptions.CreateFromAttributeDeclarations(typeInfo.Type, out _); typeClassifierFactory = null; } else if (polymorphismOptions.IsEmpty) { // An empty options instance indicates no polymorphism metadata was provided at compile time. polymorphismOptions = null; typeClassifierFactory = null; } if (polymorphismOptions is not null) { typeInfo.SetPolymorphismOptions(polymorphismOptions); if (typeClassifierFactory is not null || typeInfo.Options.TypeClassifiers.Count > 0) { typeInfo.TypeClassifierFactory = typeClassifierFactory; typeInfo.TypeClassifierResolutionPending = true; } } } private static void PopulateParameterInfoValues(JsonTypeInfo typeInfo, Func<JsonParameterInfoValues[]?>? paramFactory) { Debug.Assert(typeInfo.Kind is JsonTypeInfoKind.Object); Debug.Assert(!typeInfo.IsReadOnly); if (paramFactory?.Invoke() is JsonParameterInfoValues[] parameterInfoValues) { typeInfo.PopulateParameterInfoValues(parameterInfoValues); } else { typeInfo.PropertyMetadataSerializationNotSupported = true; } } internal static void PopulateProperties(JsonTypeInfo typeInfo, JsonTypeInfo.JsonPropertyInfoList propertyList, Func<JsonSerializerContext, JsonPropertyInfo[]> propInitFunc) { Debug.Assert(typeInfo.Kind is JsonTypeInfoKind.Object); Debug.Assert(!typeInfo.IsConfigured); Debug.Assert(typeInfo.Type != JsonTypeInfo.ObjectType); Debug.Assert(typeInfo.Converter.ElementType is null); JsonSerializerContext? context = typeInfo.Options.TypeInfoResolver as JsonSerializerContext; JsonPropertyInfo[] properties = propInitFunc(context!); // Regardless of the source generator we need to re-run the naming conflict resolution algorithm // at run time since it is possible that the naming policy or other configs can be different then. JsonTypeInfo.PropertyHierarchyResolutionState state = new(typeInfo.Options); foreach (JsonPropertyInfo jsonPropertyInfo in properties) { if (!jsonPropertyInfo.SrcGen_IsPublic) { if (jsonPropertyInfo.SrcGen_HasJsonInclude) { if (jsonPropertyInfo.Get is null && jsonPropertyInfo.Set is null) { // [JsonInclude] property is inaccessible and the source generator // did not provide getter/setter delegates (e.g. older generator). Debug.Assert(jsonPropertyInfo.MemberName is not null, "MemberName is not set by source gen"); ThrowHelper.ThrowInvalidOperationException_JsonIncludeOnInaccessibleProperty(jsonPropertyInfo.MemberName, jsonPropertyInfo.DeclaringType); } // Non-public [JsonInclude] property with getter/setter delegates provided // via UnsafeAccessor or reflection fallback — treat it as a valid property. } else { continue; } } if (jsonPropertyInfo.MemberType == MemberTypes.Field && !jsonPropertyInfo.SrcGen_HasJsonInclude && !typeInfo.Options.IncludeFields) { continue; } propertyList.AddPropertyWithConflictResolution(jsonPropertyInfo, ref state); } if (state.IsPropertyOrderSpecified) { propertyList.SortProperties(); } } private static JsonPropertyInfo<T> CreatePropertyInfoCore<T>(JsonPropertyInfoValues<T> propertyInfoValues, JsonSerializerOptions options) { var propertyInfo = new JsonPropertyInfo<T>(propertyInfoValues.DeclaringType, declaringTypeInfo: null, options); DeterminePropertyName(propertyInfo, declaredPropertyName: propertyInfoValues.PropertyName, declaredJsonPropertyName: propertyInfoValues.JsonPropertyName); propertyInfo.MemberName = propertyInfoValues.PropertyName; propertyInfo.MemberType = propertyInfoValues.IsProperty ? MemberTypes.Property : MemberTypes.Field; propertyInfo.SrcGen_IsPublic = propertyInfoValues.IsPublic; propertyInfo.SrcGen_HasJsonInclude = propertyInfoValues.HasJsonInclude; propertyInfo.IsExtensionData = propertyInfoValues.IsExtensionData; propertyInfo.CustomConverter = propertyInfoValues.Converter; if (propertyInfo.IgnoreCondition != JsonIgnoreCondition.Always) { propertyInfo.Get = propertyInfoValues.Getter!; propertyInfo.Set = propertyInfoValues.Setter; } propertyInfo.IgnoreCondition = propertyInfoValues.IgnoreCondition; propertyInfo.JsonTypeInfo = propertyInfoValues.PropertyTypeInfo; propertyInfo.NumberHandling = propertyInfoValues.NumberHandling; propertyInfo.AttributeProviderFactory = propertyInfoValues.AttributeProviderFactory; return propertyInfo; } private static void DeterminePropertyName( JsonPropertyInfo propertyInfo, string declaredPropertyName, string? declaredJsonPropertyName) { string? name; // Property name settings. if (declaredJsonPropertyName is not null) { name = declaredJsonPropertyName; } else if (propertyInfo.Options.PropertyNamingPolicy is null) { name = declaredPropertyName; } else { name = propertyInfo.Options.PropertyNamingPolicy.ConvertName(declaredPropertyName); } // Compat: We need to do validation before we assign Name so that we get InvalidOperationException rather than ArgumentNullException if (name is null) { ThrowHelper.ThrowInvalidOperationException_SerializerPropertyNameNull(propertyInfo); } propertyInfo.Name = name; } } }