| File: System\Text\Json\Serialization\ReadStackFrame.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; using System.Collections.Generic; using System.Diagnostics; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using System.Text.Json.Serialization; using System.Text.Json.Serialization.Metadata; namespace System.Text.Json { [StructLayout(LayoutKind.Auto)] [DebuggerDisplay("{DebuggerDisplay,nq}")] internal struct ReadStackFrame { // Current property values. public JsonPropertyInfo? JsonPropertyInfo; public StackFramePropertyState PropertyState; public bool UseExtensionProperty; // Support JSON Path on exceptions and non-string Dictionary keys. // This is Utf8 since we don't want to convert to string until an exception is thrown. // For dictionary keys we don't want to convert to TKey until we have both key and value when parsing the dictionary elements on stream cases. public byte[]? JsonPropertyName; public string? JsonPropertyNameAsString; // This is used for string dictionary keys and re-entry cases that specify a property name. // Stores the non-string dictionary keys for continuation. public object? DictionaryKey; /// <summary> /// Records the Utf8JsonReader Depth at the start of the current value. /// </summary> public int OriginalDepth; #if DEBUG /// <summary> /// Records the Utf8JsonReader TokenType at the start of the current value. /// Only used to validate debug builds. /// </summary> public JsonTokenType OriginalTokenType; #endif // Current object (POCO or IEnumerable). public object? ReturnValue; // The current return value used for re-entry. public JsonTypeInfo JsonTypeInfo; public StackFrameObjectState ObjectState; // State tracking the current object. // Current object can contain metadata public bool CanContainMetadata; public MetadataPropertyName LatestMetadataPropertyName; public MetadataPropertyName MetadataPropertyNames; // Serialization state for value serialized by the current frame. public PolymorphicSerializationState PolymorphicSerializationState; // Holds any entered polymorphic JsonTypeInfo metadata. public JsonTypeInfo? PolymorphicJsonTypeInfo; // Gets the initial JsonTypeInfo metadata used when deserializing the current value. public JsonTypeInfo BaseJsonTypeInfo => PolymorphicSerializationState == PolymorphicSerializationState.PolymorphicReEntryStarted ? PolymorphicJsonTypeInfo! : JsonTypeInfo; // For performance, we order the properties by the first deserialize and PropertyIndex helps find the right slot quicker. public int PropertyIndex; // Tracks newly encounentered UTF-8 encoded properties during the current deserialization, to be appended to the cache. public PropertyRefCacheBuilder? PropertyRefCacheBuilder; // Holds relevant state when deserializing objects with parameterized constructors. public ArgumentState? CtorArgumentState; // Whether to use custom number handling. public JsonNumberHandling? NumberHandling; // Represents known (non-extension) properties which have value assigned. // Each bit corresponds to a property. // False means that property is not set (not yet occurred in the payload). // Length of the BitArray is equal to number of non-extension properties. // Every JsonPropertyInfo has PropertyIndex property which maps to an index in this BitArray. public BitArray? AssignedProperties; // Tracks state related to property population. public bool HasParentObject; public bool IsPopulating; public void EndConstructorParameter() { CtorArgumentState!.JsonParameterInfo = null; JsonPropertyName = null; PropertyState = StackFramePropertyState.None; } public void EndProperty() { JsonPropertyInfo = null!; JsonPropertyName = null; JsonPropertyNameAsString = null; PropertyState = StackFramePropertyState.None; // No need to clear these since they are overwritten each time: // NumberHandling // UseExtensionProperty } public void EndElement() { JsonPropertyNameAsString = null; PropertyState = StackFramePropertyState.None; } /// <summary> /// Is the current object a Dictionary. /// </summary> public bool IsProcessingDictionary() => JsonTypeInfo.Kind is JsonTypeInfoKind.Dictionary; /// <summary> /// Is the current object an Enumerable. /// </summary> public bool IsProcessingEnumerable() => JsonTypeInfo.Kind is JsonTypeInfoKind.Enumerable; [MethodImpl(MethodImplOptions.AggressiveInlining)] public void MarkPropertyAsRead(JsonPropertyInfo propertyInfo) { if (AssignedProperties is { }) { if (!propertyInfo.Options.AllowDuplicateProperties) { if (AssignedProperties[propertyInfo.PropertyIndex]) { ThrowHelper.ThrowJsonException_DuplicatePropertyNotAllowed(propertyInfo); } } AssignedProperties[propertyInfo.PropertyIndex] = true; } } [MethodImpl(MethodImplOptions.AggressiveInlining)] internal void InitializePropertiesValidationState(JsonTypeInfo typeInfo) { Debug.Assert(AssignedProperties is null); if (typeInfo.ShouldTrackRequiredProperties || !typeInfo.Options.AllowDuplicateProperties) { // This may be slightly larger than required (e.g. if there's an extension property) AssignedProperties = new BitArray(typeInfo.Properties.Count); } } [MethodImpl(MethodImplOptions.AggressiveInlining)] internal void ValidateAllRequiredPropertiesAreRead(JsonTypeInfo typeInfo) { if (typeInfo.ShouldTrackRequiredProperties) { Debug.Assert(AssignedProperties is not null); Debug.Assert(typeInfo.OptionalPropertiesMask is not null); // All properties must be either assigned or optional BitArray assignedOrNotRequiredPropertiesSet = AssignedProperties.Or(typeInfo.OptionalPropertiesMask); if (!assignedOrNotRequiredPropertiesSet.HasAllSet()) { ThrowHelper.ThrowJsonException_JsonRequiredPropertyMissing(typeInfo, assignedOrNotRequiredPropertiesSet); } } AssignedProperties = null; } [DebuggerBrowsable(DebuggerBrowsableState.Never)] private string DebuggerDisplay => $"ConverterStrategy.{JsonTypeInfo?.Converter.ConverterStrategy}, {JsonTypeInfo?.Type.Name}"; } }