| File: System\Text\Json\Serialization\JsonConverter.MetadataHandling.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.Text.Json.Serialization.Metadata; namespace System.Text.Json.Serialization { public partial class JsonConverter { /// <summary> /// Initializes the state for polymorphic cases and returns the appropriate derived converter. /// </summary> internal JsonConverter? ResolvePolymorphicConverter(JsonTypeInfo jsonTypeInfo, ref ReadStack state) { Debug.Assert(!IsValueType); Debug.Assert(CanHaveMetadata); Debug.Assert((state.Current.MetadataPropertyNames & MetadataPropertyName.Type) != 0 || state.PolymorphicResolvedType is not null); Debug.Assert(state.Current.PolymorphicSerializationState != PolymorphicSerializationState.PolymorphicReEntryStarted); JsonConverter? polymorphicConverter = null; switch (state.Current.PolymorphicSerializationState) { case PolymorphicSerializationState.None: Debug.Assert(!state.IsContinuation); PolymorphicTypeResolver resolver = jsonTypeInfo.PolymorphicTypeResolver!; JsonTypeInfo? resolvedType; if (state.PolymorphicResolvedType is Type classifiedType) { // Type resolved by custom TypeClassifier — resolve directly. resolver.TryResolveDerivedJsonTypeInfo(classifiedType, out resolvedType); state.PolymorphicResolvedType = null; } else { // Standard discriminator-based resolution. Debug.Assert(state.PolymorphicTypeDiscriminator != null); Debug.Assert(resolver.UsesTypeDiscriminators); resolver.TryGetDerivedJsonTypeInfo(state.PolymorphicTypeDiscriminator, out resolvedType); } if (resolvedType is not null) { Debug.Assert(Type!.IsAssignableFrom(resolvedType.Type)); polymorphicConverter = state.InitializePolymorphicReEntry(resolvedType); if (!polymorphicConverter.CanHaveMetadata) { ThrowHelper.ThrowNotSupportedException_DerivedConverterDoesNotSupportMetadata(resolvedType.Type); } } else { state.Current.PolymorphicSerializationState = PolymorphicSerializationState.PolymorphicReEntryNotFound; } state.PolymorphicTypeDiscriminator = null; break; case PolymorphicSerializationState.PolymorphicReEntrySuspended: polymorphicConverter = state.ResumePolymorphicReEntry(); Debug.Assert(Type!.IsAssignableFrom(polymorphicConverter.Type)); break; case PolymorphicSerializationState.PolymorphicReEntryNotFound: Debug.Assert(state.Current.PolymorphicJsonTypeInfo is null); break; default: Debug.Fail("Unexpected PolymorphicSerializationState."); break; } return polymorphicConverter; } /// <summary> /// Initializes the state for polymorphic cases and returns the appropriate derived converter. /// </summary> internal JsonConverter? ResolvePolymorphicConverter(object value, JsonTypeInfo jsonTypeInfo, JsonSerializerOptions options, ref WriteStack state) { Debug.Assert(!IsValueType); Debug.Assert(value != null && Type!.IsAssignableFrom(value.GetType())); Debug.Assert(CanBePolymorphic || jsonTypeInfo.PolymorphicTypeResolver != null); Debug.Assert(state.PolymorphicTypeDiscriminator is null); JsonConverter? polymorphicConverter = null; switch (state.Current.PolymorphicSerializationState) { case PolymorphicSerializationState.None: Debug.Assert(!state.IsContinuation); Type runtimeType = value.GetType(); if (CanBePolymorphic && runtimeType != Type) { Debug.Assert(Type == typeof(object)); jsonTypeInfo = state.Current.InitializePolymorphicReEntry(runtimeType, options); polymorphicConverter = jsonTypeInfo.Converter; } if (jsonTypeInfo.PolymorphicTypeResolver is PolymorphicTypeResolver resolver) { Debug.Assert(jsonTypeInfo.Converter.CanHaveMetadata); if (resolver.TryGetDerivedJsonTypeInfo(runtimeType, out JsonTypeInfo? derivedJsonTypeInfo, out object? typeDiscriminator)) { polymorphicConverter = state.Current.InitializePolymorphicReEntry(derivedJsonTypeInfo); if (typeDiscriminator is not null) { if (!polymorphicConverter.CanHaveMetadata) { ThrowHelper.ThrowNotSupportedException_DerivedConverterDoesNotSupportMetadata(derivedJsonTypeInfo.Type); } state.PolymorphicTypeDiscriminator = typeDiscriminator; state.PolymorphicTypeResolver = resolver; } } } if (polymorphicConverter is null) { state.Current.PolymorphicSerializationState = PolymorphicSerializationState.PolymorphicReEntryNotFound; } break; case PolymorphicSerializationState.PolymorphicReEntrySuspended: Debug.Assert(state.IsContinuation); polymorphicConverter = state.Current.ResumePolymorphicReEntry(); Debug.Assert(Type.IsAssignableFrom(polymorphicConverter.Type)); break; case PolymorphicSerializationState.PolymorphicReEntryNotFound: Debug.Assert(state.IsContinuation); break; default: Debug.Fail("Unexpected PolymorphicSerializationState."); break; } return polymorphicConverter; } internal bool TryHandleSerializedObjectReference(Utf8JsonWriter writer, object value, JsonSerializerOptions options, JsonConverter? polymorphicConverter, ref WriteStack state) { Debug.Assert(!IsValueType); Debug.Assert(!state.IsContinuation); Debug.Assert(value != null); switch (options.ReferenceHandlingStrategy) { case JsonKnownReferenceHandler.IgnoreCycles: ReferenceResolver resolver = state.ReferenceResolver; if (resolver.ContainsReferenceForCycleDetection(value)) { writer.WriteNullValue(); if (polymorphicConverter is not null) { // Clear out any polymorphic state. state.PolymorphicTypeDiscriminator = null; state.PolymorphicTypeResolver = null; } return true; } resolver.PushReferenceForCycleDetection(value); // WriteStack reuses root-level stack frames for its children as a performance optimization; // we want to avoid writing any data for the root-level object to avoid corrupting the stack. // This is fine since popping the root object at the end of serialization is not essential. state.Current.IsPushedReferenceForCycleDetection = state.CurrentDepth > 0; break; case JsonKnownReferenceHandler.Preserve: bool canHaveIdMetadata = polymorphicConverter?.CanHaveMetadata ?? CanHaveMetadata; if (canHaveIdMetadata && JsonSerializer.TryGetReferenceForValue(value, ref state, writer)) { // We found a repeating reference and wrote the relevant metadata; serialization complete. return true; } break; default: Debug.Fail("Unexpected ReferenceHandlingStrategy."); break; } return false; } } }