Beispiel #1
0
        internal static MetadataPropertyName WriteMetadataForObject(
            JsonConverter jsonConverter,
            ref WriteStack state,
            Utf8JsonWriter writer)
        {
            Debug.Assert(jsonConverter.CanHaveMetadata);
            Debug.Assert(!state.IsContinuation);
            Debug.Assert(state.CurrentContainsMetadata);

            MetadataPropertyName writtenMetadata = MetadataPropertyName.None;

            if (state.NewReferenceId != null)
            {
                writer.WriteString(s_metadataId, state.NewReferenceId);
                writtenMetadata     |= MetadataPropertyName.Id;
                state.NewReferenceId = null;
            }

            if (state.PolymorphicTypeDiscriminator is string typeDiscriminatorId)
            {
                Debug.Assert(state.Parent.JsonPropertyInfo !.JsonTypeInfo.PolymorphicTypeResolver != null);

                JsonEncodedText propertyName =
                    state.Parent.JsonPropertyInfo.JsonTypeInfo.PolymorphicTypeResolver.CustomTypeDiscriminatorPropertyNameJsonEncoded is JsonEncodedText customPropertyName
                    ? customPropertyName
                    : s_metadataType;

                writer.WriteString(propertyName, typeDiscriminatorId);
                writtenMetadata |= MetadataPropertyName.Type;
                state.PolymorphicTypeDiscriminator = null;
            }

            Debug.Assert(writtenMetadata != MetadataPropertyName.None);
            return(writtenMetadata);
        }
Beispiel #2
0
        internal static MetadataPropertyName WriteReferenceForCollection(
            JsonConverter jsonConverter,
            object currentValue,
            ref WriteStack state,
            Utf8JsonWriter writer)
        {
            MetadataPropertyName metadataToWrite = GetResolvedReferenceHandling(jsonConverter, currentValue, ref state, out string?referenceId);

            if (metadataToWrite == MetadataPropertyName.NoMetadata)
            {
                writer.WriteStartArray();
            }
            else if (metadataToWrite == MetadataPropertyName.Id)
            {
                writer.WriteStartObject();
                writer.WriteString(s_metadataId, referenceId !);
                writer.WriteStartArray(s_metadataValues);
            }
            else
            {
                Debug.Assert(metadataToWrite == MetadataPropertyName.Ref);
                writer.WriteStartObject();
                writer.WriteString(s_metadataRef, referenceId !);
                writer.WriteEndObject();
            }

            return(metadataToWrite);
        }
        private static void HandleMetadataPropertyValue(ref Utf8JsonReader reader, ref ReadStack state)
        {
            Debug.Assert(state.Current.JsonClassInfo !.Options.ReferenceHandling.ShouldReadPreservedReferences());

            if (reader.TokenType != JsonTokenType.String)
            {
                ThrowHelper.ThrowJsonException_MetadataValueWasNotString(reader.TokenType);
            }

            MetadataPropertyName metadata = state.Current.LastSeenMetadataProperty;
            string key = reader.GetString() !;

            Debug.Assert(metadata == MetadataPropertyName.Id || metadata == MetadataPropertyName.Ref);

            if (metadata == MetadataPropertyName.Id)
            {
                // Special case for dictionary properties since those do not push into the ReadStack.
                // There is no need to check for enumerables since those will always be wrapped into JsonPreservableArrayReference<T> which turns enumerables into objects.
                object value = state.Current.IsProcessingProperty(ClassType.Dictionary) ?
                               state.Current.JsonPropertyInfo !.GetValueAsObject(state.Current.ReturnValue) ! :
                               state.Current.ReturnValue !;

                state.ReferenceResolver.AddReferenceOnDeserialize(key, value);
            }
            else if (metadata == MetadataPropertyName.Ref)
            {
                state.Current.ReferenceId = key;
            }
        }
Beispiel #4
0
        internal sealed override bool OnTryWrite(Utf8JsonWriter writer, TCollection value, JsonSerializerOptions options, ref WriteStack state)
        {
            bool success;

            if (value == null)
            {
                writer.WriteNullValue();
                success = true;
            }
            else
            {
                bool shouldWritePreservedReferences = options.ReferenceHandling.ShouldWritePreservedReferences();

                if (!state.Current.ProcessedStartToken)
                {
                    state.Current.ProcessedStartToken = true;

                    if (!shouldWritePreservedReferences)
                    {
                        writer.WriteStartArray();
                    }
                    else
                    {
                        MetadataPropertyName metadata = JsonSerializer.WriteReferenceForCollection(this, value, ref state, writer);
                        if (metadata == MetadataPropertyName.Ref)
                        {
                            return(true);
                        }

                        state.Current.MetadataPropertyName = metadata;
                    }

                    state.Current.DeclaredJsonPropertyInfo = state.Current.JsonClassInfo.ElementClassInfo !.PropertyInfoForClassInfo;
                }

                success = OnWriteResume(writer, value, options, ref state);
                if (success)
                {
                    if (!state.Current.ProcessedEndToken)
                    {
                        state.Current.ProcessedEndToken = true;
                        writer.WriteEndArray();

                        if (state.Current.MetadataPropertyName == MetadataPropertyName.Id)
                        {
                            // Write the EndObject for $values.
                            writer.WriteEndObject();
                        }
                    }
                }
            }

            return(success);
        }
Beispiel #5
0
        internal static MetadataPropertyName WriteMetadataForCollection(
            JsonConverter jsonConverter,
            ref WriteStack state,
            Utf8JsonWriter writer)
        {
            // For collections with metadata, we nest the array payload within a JSON object.
            writer.WriteStartObject();
            MetadataPropertyName writtenMetadata = WriteMetadataForObject(jsonConverter, ref state, writer);

            writer.WritePropertyName(s_metadataValues); // property name containing nested array values.
            return(writtenMetadata);
        }
        private static void ResolveMetadataOnObject(MetadataPropertyName metadata, ref ReadStack state)
        {
            if (metadata == MetadataPropertyName.Id)
            {
                if (state.Current.PropertyIndex > 0 || state.Current.LastSeenMetadataProperty != MetadataPropertyName.NoMetadata)
                {
                    ThrowHelper.ThrowJsonException_MetadataIdIsNotFirstProperty();
                }

                state.Current.JsonPropertyName = ReadStack.s_idMetadataPropertyName;
            }
            else if (metadata == MetadataPropertyName.Values)
            {
                JsonPropertyInfo info = GetValuesPropertyInfoFromJsonPreservableArrayRef(ref state.Current);
                state.Current.JsonPropertyName = ReadStack.s_valuesMetadataPropertyName;
                state.Current.JsonPropertyInfo = info;

                // Throw after setting JsonPropertyName to show the correct JSON Path.
                if (state.Current.LastSeenMetadataProperty != MetadataPropertyName.Id)
                {
                    ThrowHelper.ThrowJsonException_MetadataMissingIdBeforeValues();
                }
            }
            else
            {
                Debug.Assert(metadata == MetadataPropertyName.Ref);

                if (state.Current.JsonClassInfo !.Type.IsValueType)
                {
                    ThrowHelper.ThrowJsonException_MetadataInvalidReferenceToValueType(state.Current.JsonClassInfo.Type);
                }

                if (state.Current.PropertyIndex > 0 || state.Current.LastSeenMetadataProperty != MetadataPropertyName.NoMetadata)
                {
                    ThrowHelper.ThrowJsonException_MetadataReferenceObjectCannotContainOtherProperties();
                }

                state.Current.JsonPropertyName = ReadStack.s_refMetadataPropertyName;
            }
        }
        private static void ResolveMetadataOnDictionary(MetadataPropertyName metadata, ref ReadStack state)
        {
            if (metadata == MetadataPropertyName.Id)
            {
                // Check we are not parsing into an immutable dictionary.
                if (state.Current.JsonPropertyInfo !.DictionaryConverter != null)
                {
                    ThrowHelper.ThrowJsonException_MetadataCannotParsePreservedObjectIntoImmutable(state.Current.JsonPropertyInfo.DeclaredPropertyType);
                }

                if (state.Current.KeyName != null)
                {
                    ThrowHelper.ThrowJsonException_MetadataIdIsNotFirstProperty_Dictionary(ref state.Current);
                }
            }
            else if (metadata == MetadataPropertyName.Ref)
            {
                if (state.Current.KeyName != null)
                {
                    ThrowHelper.ThrowJsonException_MetadataReferenceObjectCannotContainOtherProperties_Dictionary(ref state.Current);
                }
            }
        }
Beispiel #8
0
        /// <summary>
        /// Returns true if successful, false is the reader ran out of buffer.
        /// Sets state.Current.ReturnValue to the $ref target for MetadataRefProperty cases.
        /// </summary>
        internal static bool ResolveMetadata(
            JsonConverter converter,
            ref Utf8JsonReader reader,
            ref ReadStack state)
        {
            if (state.Current.ObjectState < StackFrameObjectState.ReadAheadNameOrEndObject)
            {
                // Read the first metadata property name.
                if (!ReadAheadMetataDataAndSetState(ref reader, ref state, StackFrameObjectState.ReadNameOrEndObject))
                {
                    return(false);
                }
            }

            if (state.Current.ObjectState == StackFrameObjectState.ReadNameOrEndObject)
            {
                if (reader.TokenType != JsonTokenType.PropertyName)
                {
                    // An enumerable needs metadata since it starts with StartObject.
                    if (converter.ClassType == ClassType.Enumerable)
                    {
                        ThrowHelper.ThrowJsonException_MetadataPreservedArrayValuesNotFound(converter.TypeToConvert);
                    }

                    // The reader should have detected other invalid cases.
                    Debug.Assert(reader.TokenType == JsonTokenType.EndObject);

                    // Skip the read of the first property name, since we already read it above.
                    state.Current.PropertyState = StackFramePropertyState.ReadName;

                    return(true);
                }

                ReadOnlySpan <byte>  propertyName = reader.GetSpan();
                MetadataPropertyName metadata     = GetMetadataPropertyName(propertyName);
                if (metadata == MetadataPropertyName.Id)
                {
                    state.Current.JsonPropertyName = propertyName.ToArray();
                    if (!converter.CanHaveIdMetadata)
                    {
                        ThrowHelper.ThrowJsonException_MetadataCannotParsePreservedObjectIntoImmutable(converter.TypeToConvert);
                    }

                    state.Current.ObjectState = StackFrameObjectState.ReadAheadIdValue;
                }
                else if (metadata == MetadataPropertyName.Ref)
                {
                    state.Current.JsonPropertyName = propertyName.ToArray();
                    if (converter.IsValueType)
                    {
                        ThrowHelper.ThrowJsonException_MetadataInvalidReferenceToValueType(converter.TypeToConvert);
                    }

                    state.Current.ObjectState = StackFrameObjectState.ReadAheadRefValue;
                }
                else if (metadata == MetadataPropertyName.Values)
                {
                    state.Current.JsonPropertyName = propertyName.ToArray();
                    if (converter.ClassType == ClassType.Enumerable)
                    {
                        ThrowHelper.ThrowJsonException_MetadataMissingIdBeforeValues();
                    }
                    else
                    {
                        ThrowHelper.ThrowJsonException_MetadataInvalidPropertyWithLeadingDollarSign(propertyName, ref state, reader);
                    }
                }
                else
                {
                    Debug.Assert(metadata == MetadataPropertyName.NoMetadata);

                    // Having a StartObject without metadata properties is not allowed.
                    if (converter.ClassType == ClassType.Enumerable)
                    {
                        state.Current.JsonPropertyName = propertyName.ToArray();
                        ThrowHelper.ThrowJsonException_MetadataPreservedArrayInvalidProperty(converter.TypeToConvert, reader);
                    }

                    // Skip the read of the first property name, since we already read it above.
                    state.Current.PropertyState = StackFramePropertyState.ReadName;
                    return(true);
                }
            }

            if (state.Current.ObjectState == StackFrameObjectState.ReadAheadRefValue)
            {
                if (!ReadAheadMetataDataAndSetState(ref reader, ref state, StackFrameObjectState.ReadRefValue))
                {
                    return(false);
                }
            }
            else if (state.Current.ObjectState == StackFrameObjectState.ReadAheadIdValue)
            {
                if (!ReadAheadMetataDataAndSetState(ref reader, ref state, StackFrameObjectState.ReadIdValue))
                {
                    return(false);
                }
            }

            if (state.Current.ObjectState == StackFrameObjectState.ReadRefValue)
            {
                if (reader.TokenType != JsonTokenType.String)
                {
                    ThrowHelper.ThrowJsonException_MetadataValueWasNotString(reader.TokenType);
                }

                string key = reader.GetString() !;

                // todo: https://github.com/dotnet/runtime/issues/32354
                state.Current.ReturnValue = state.ReferenceResolver.ResolveReferenceOnDeserialize(key);
                state.Current.ObjectState = StackFrameObjectState.ReadAheadRefEndObject;
            }
            else if (state.Current.ObjectState == StackFrameObjectState.ReadIdValue)
            {
                if (reader.TokenType != JsonTokenType.String)
                {
                    ThrowHelper.ThrowJsonException_MetadataValueWasNotString(reader.TokenType);
                }

                state.Current.MetadataId = reader.GetString();

                // Clear the MetadataPropertyName since we are done processing Id.
                state.Current.JsonPropertyName = default;

                if (converter.ClassType == ClassType.Enumerable)
                {
                    // Need to Read $values property name.
                    state.Current.ObjectState = StackFrameObjectState.ReadAheadValuesName;
                }
                else
                {
                    // We are done reading metadata.
                    state.Current.ObjectState = StackFrameObjectState.PropertyValue;
                    return(true);
                }
            }

            if (state.Current.ObjectState == StackFrameObjectState.ReadAheadRefEndObject)
            {
                if (!ReadAheadMetataDataAndSetState(ref reader, ref state, StackFrameObjectState.ReadRefEndObject))
                {
                    return(false);
                }
            }

            if (state.Current.ObjectState == StackFrameObjectState.ReadRefEndObject)
            {
                if (reader.TokenType != JsonTokenType.EndObject)
                {
                    // We just read a property. The only valid next tokens are EndObject and PropertyName.
                    Debug.Assert(reader.TokenType == JsonTokenType.PropertyName);

                    ThrowHelper.ThrowJsonException_MetadataReferenceObjectCannotContainOtherProperties(reader.GetSpan(), ref state);
                }

                return(true);
            }

            if (state.Current.ObjectState == StackFrameObjectState.ReadAheadValuesName)
            {
                if (!ReadAheadMetataDataAndSetState(ref reader, ref state, StackFrameObjectState.ReadValuesName))
                {
                    return(false);
                }
            }

            if (state.Current.ObjectState == StackFrameObjectState.ReadValuesName)
            {
                if (reader.TokenType != JsonTokenType.PropertyName)
                {
                    ThrowHelper.ThrowJsonException_MetadataPreservedArrayValuesNotFound(converter.TypeToConvert);
                }

                ReadOnlySpan <byte> propertyName = reader.GetSpan();

                // Remember the property in case we get an exception.
                state.Current.JsonPropertyName = propertyName.ToArray();

                if (GetMetadataPropertyName(propertyName) != MetadataPropertyName.Values)
                {
                    ThrowHelper.ThrowJsonException_MetadataPreservedArrayInvalidProperty(converter.TypeToConvert, reader);
                }

                state.Current.ObjectState = StackFrameObjectState.ReadAheadValuesStartArray;
            }

            if (state.Current.ObjectState == StackFrameObjectState.ReadAheadValuesStartArray)
            {
                if (!ReadAheadMetataDataAndSetState(ref reader, ref state, StackFrameObjectState.ReadValuesStartArray))
                {
                    return(false);
                }
            }

            if (state.Current.ObjectState == StackFrameObjectState.ReadValuesStartArray)
            {
                if (reader.TokenType != JsonTokenType.StartArray)
                {
                    ThrowHelper.ThrowJsonException_MetadataValuesInvalidToken(reader.TokenType);
                }

                state.Current.ObjectState = StackFrameObjectState.PropertyValue;
            }

            return(true);
        }
        private static void HandlePropertyName(
            JsonSerializerOptions options,
            ref Utf8JsonReader reader,
            ref ReadStack state)
        {
            if (state.Current.Drain)
            {
                return;
            }

            Debug.Assert(state.Current.ReturnValue != null || state.Current.TempDictionaryValues != null);
            Debug.Assert(state.Current.JsonClassInfo != null);

            bool isProcessingDictObject = state.Current.IsProcessingObject(ClassType.Dictionary);

            if ((isProcessingDictObject || state.Current.IsProcessingProperty(ClassType.Dictionary)) &&
                state.Current.JsonClassInfo.DataExtensionProperty != state.Current.JsonPropertyInfo)
            {
                if (isProcessingDictObject)
                {
                    state.Current.JsonPropertyInfo = state.Current.JsonClassInfo.PolicyProperty;
                }

                if (options.ReferenceHandling.ShouldReadPreservedReferences())
                {
                    ReadOnlySpan <byte>  propertyName = reader.HasValueSequence ? reader.ValueSequence.ToArray() : reader.ValueSpan;
                    MetadataPropertyName metadata     = GetMetadataPropertyName(propertyName, ref state, ref reader);
                    ResolveMetadataOnDictionary(metadata, ref state);

                    state.Current.LastSeenMetadataProperty = metadata;
                }

                state.Current.KeyName = reader.GetString();
            }
            else
            {
                Debug.Assert(state.Current.JsonClassInfo.ClassType == ClassType.Object);

                state.Current.EndProperty();

                ReadOnlySpan <byte> propertyName = reader.HasValueSequence ? reader.ValueSequence.ToArray() : reader.ValueSpan;
                if (options.ReferenceHandling.ShouldReadPreservedReferences())
                {
                    MetadataPropertyName metadata = GetMetadataPropertyName(propertyName, ref state, ref reader);

                    if (metadata == MetadataPropertyName.NoMetadata)
                    {
                        if (state.Current.IsPreservedArray)
                        {
                            ThrowHelper.ThrowJsonException_MetadataPreservedArrayInvalidProperty(in reader, ref state);
                        }

                        HandlePropertyNameDefault(propertyName, ref state, ref reader, options);
                    }
                    else
                    {
                        ResolveMetadataOnObject(metadata, ref state);
                    }

                    state.Current.LastSeenMetadataProperty = metadata;
                }
                else
                {
                    HandlePropertyNameDefault(propertyName, ref state, ref reader, options);
                }
            }
        }
Beispiel #10
0
        /// <summary>
        /// Returns true if successful, false is the reader ran out of buffer.
        /// Sets state.Current.ReturnValue to the reference target for $ref cases;
        /// Sets state.Current.ReturnValue to a new instance for $id cases.
        /// </summary>
        internal static bool ResolveMetadataForJsonObject <T>(
            ref Utf8JsonReader reader,
            ref ReadStack state,
            JsonSerializerOptions options)
        {
            JsonConverter converter = state.Current.JsonTypeInfo.PropertyInfoForTypeInfo.ConverterBase;

            if (state.Current.ObjectState < StackFrameObjectState.ReadAheadNameOrEndObject)
            {
                // Read the first metadata property name.
                if (!TryReadAheadMetadataAndSetState(ref reader, ref state, StackFrameObjectState.ReadNameOrEndObject))
                {
                    return(false);
                }
            }

            if (state.Current.ObjectState == StackFrameObjectState.ReadNameOrEndObject)
            {
                if (reader.TokenType != JsonTokenType.PropertyName)
                {
                    // Since this was an empty object, we are done reading metadata.
                    state.Current.ObjectState = StackFrameObjectState.PropertyValue;
                    // Skip the read of the first property name, since we already read it above.
                    state.Current.PropertyState = StackFramePropertyState.ReadName;
                    return(true);
                }

                ReadOnlySpan <byte>  propertyName = reader.GetSpan();
                MetadataPropertyName metadata     = GetMetadataPropertyName(propertyName);
                if (metadata == MetadataPropertyName.Id)
                {
                    state.Current.JsonPropertyName = s_idPropertyName;

                    if (!converter.CanHaveIdMetadata)
                    {
                        ThrowHelper.ThrowJsonException_MetadataCannotParsePreservedObjectIntoImmutable(converter.TypeToConvert);
                    }

                    state.Current.ObjectState = StackFrameObjectState.ReadAheadIdValue;
                }
                else if (metadata == MetadataPropertyName.Ref)
                {
                    state.Current.JsonPropertyName = s_refPropertyName;

                    if (converter.IsValueType)
                    {
                        ThrowHelper.ThrowJsonException_MetadataInvalidReferenceToValueType(converter.TypeToConvert);
                    }

                    state.Current.ObjectState = StackFrameObjectState.ReadAheadRefValue;
                }
                else if (metadata == MetadataPropertyName.Values)
                {
                    ThrowHelper.ThrowJsonException_MetadataInvalidPropertyWithLeadingDollarSign(propertyName, ref state, reader);
                }
                else
                {
                    Debug.Assert(metadata == MetadataPropertyName.NoMetadata);
                    // We are done reading metadata, the object didn't contain any.
                    state.Current.ObjectState = StackFrameObjectState.PropertyValue;
                    // Skip the read of the first property name, since we already read it above.
                    state.Current.PropertyState = StackFramePropertyState.ReadName;
                    return(true);
                }
            }

            if (state.Current.ObjectState == StackFrameObjectState.ReadAheadRefValue)
            {
                if (!TryReadAheadMetadataAndSetState(ref reader, ref state, StackFrameObjectState.ReadRefValue))
                {
                    return(false);
                }
            }
            else if (state.Current.ObjectState == StackFrameObjectState.ReadAheadIdValue)
            {
                if (!TryReadAheadMetadataAndSetState(ref reader, ref state, StackFrameObjectState.ReadIdValue))
                {
                    return(false);
                }
            }

            if (state.Current.ObjectState == StackFrameObjectState.ReadRefValue)
            {
                if (reader.TokenType != JsonTokenType.String)
                {
                    ThrowHelper.ThrowJsonException_MetadataValueWasNotString(reader.TokenType);
                }

                string referenceId = reader.GetString() !;
                object value       = state.ReferenceResolver.ResolveReference(referenceId);
                ValidateValueIsCorrectType <T>(value, referenceId);
                state.Current.ReturnValue = value;

                state.Current.ObjectState = StackFrameObjectState.ReadAheadRefEndObject;
            }
            else if (state.Current.ObjectState == StackFrameObjectState.ReadIdValue)
            {
                if (reader.TokenType != JsonTokenType.String)
                {
                    ThrowHelper.ThrowJsonException_MetadataValueWasNotString(reader.TokenType);
                }

                converter.CreateInstanceForReferenceResolver(ref reader, ref state, options);

                string referenceId = reader.GetString() !;
                state.ReferenceResolver.AddReference(referenceId, state.Current.ReturnValue !);

                // We are done reading metadata plus we instantiated the object.
                state.Current.ObjectState = StackFrameObjectState.CreatedObject;
            }

            // Clear the metadata property name that was set in case of failure on ResolveReference/AddReference.
            state.Current.JsonPropertyName = null;

            if (state.Current.ObjectState == StackFrameObjectState.ReadAheadRefEndObject)
            {
                if (!TryReadAheadMetadataAndSetState(ref reader, ref state, StackFrameObjectState.ReadRefEndObject))
                {
                    return(false);
                }
            }

            if (state.Current.ObjectState == StackFrameObjectState.ReadRefEndObject)
            {
                if (reader.TokenType != JsonTokenType.EndObject)
                {
                    // We just read a property. The only valid next tokens are EndObject and PropertyName.
                    Debug.Assert(reader.TokenType == JsonTokenType.PropertyName);

                    ThrowHelper.ThrowJsonException_MetadataReferenceObjectCannotContainOtherProperties(reader.GetSpan(), ref state);
                }
            }

            return(true);
        }
Beispiel #11
0
        /// <summary>
        /// Returns true if successful, false is the reader ran out of buffer.
        /// Sets state.Current.ReturnValue to the reference target for $ref cases;
        /// Sets state.Current.ReturnValue to a new instance for $id cases.
        /// </summary>
        internal static bool ResolveMetadataForJsonArray <T>(
            ref Utf8JsonReader reader,
            ref ReadStack state,
            JsonSerializerOptions options)
        {
            JsonConverter converter = state.Current.JsonTypeInfo.PropertyInfoForTypeInfo.ConverterBase;

            if (state.Current.ObjectState < StackFrameObjectState.ReadAheadNameOrEndObject)
            {
                // Read the first metadata property name.
                if (!TryReadAheadMetadataAndSetState(ref reader, ref state, StackFrameObjectState.ReadNameOrEndObject))
                {
                    return(false);
                }
            }

            if (state.Current.ObjectState == StackFrameObjectState.ReadNameOrEndObject)
            {
                if (reader.TokenType != JsonTokenType.PropertyName)
                {
                    // The reader should have detected other invalid cases.
                    Debug.Assert(reader.TokenType == JsonTokenType.EndObject);

                    // An enumerable needs metadata since it starts with StartObject.
                    ThrowHelper.ThrowJsonException_MetadataPreservedArrayValuesNotFound(ref state, converter.TypeToConvert);
                }

                ReadOnlySpan <byte>  propertyName = reader.GetSpan();
                MetadataPropertyName metadata     = GetMetadataPropertyName(propertyName);
                if (metadata == MetadataPropertyName.Id)
                {
                    state.Current.JsonPropertyName = s_idPropertyName;

                    if (!converter.CanHaveIdMetadata)
                    {
                        ThrowHelper.ThrowJsonException_MetadataCannotParsePreservedObjectIntoImmutable(converter.TypeToConvert);
                    }

                    state.Current.ObjectState = StackFrameObjectState.ReadAheadIdValue;
                }
                else if (metadata == MetadataPropertyName.Ref)
                {
                    state.Current.JsonPropertyName = s_refPropertyName;

                    if (converter.IsValueType)
                    {
                        ThrowHelper.ThrowJsonException_MetadataInvalidReferenceToValueType(converter.TypeToConvert);
                    }

                    state.Current.ObjectState = StackFrameObjectState.ReadAheadRefValue;
                }
                else if (metadata == MetadataPropertyName.Values)
                {
                    ThrowHelper.ThrowJsonException_MetadataMissingIdBeforeValues(ref state, propertyName);
                }
                else
                {
                    Debug.Assert(metadata == MetadataPropertyName.NoMetadata);

                    // Having a StartObject without metadata properties is not allowed.
                    ThrowHelper.ThrowJsonException_MetadataPreservedArrayInvalidProperty(ref state, converter.TypeToConvert, reader);
                }
            }

            if (state.Current.ObjectState == StackFrameObjectState.ReadAheadRefValue)
            {
                if (!TryReadAheadMetadataAndSetState(ref reader, ref state, StackFrameObjectState.ReadRefValue))
                {
                    return(false);
                }
            }
            else if (state.Current.ObjectState == StackFrameObjectState.ReadAheadIdValue)
            {
                if (!TryReadAheadMetadataAndSetState(ref reader, ref state, StackFrameObjectState.ReadIdValue))
                {
                    return(false);
                }
            }

            if (state.Current.ObjectState == StackFrameObjectState.ReadRefValue)
            {
                if (reader.TokenType != JsonTokenType.String)
                {
                    ThrowHelper.ThrowJsonException_MetadataValueWasNotString(reader.TokenType);
                }

                string referenceId = reader.GetString() !;
                object value       = state.ReferenceResolver.ResolveReference(referenceId);
                ValidateValueIsCorrectType <T>(value, referenceId);
                state.Current.ReturnValue = value;

                state.Current.ObjectState = StackFrameObjectState.ReadAheadRefEndObject;
            }
            else if (state.Current.ObjectState == StackFrameObjectState.ReadIdValue)
            {
                if (reader.TokenType != JsonTokenType.String)
                {
                    ThrowHelper.ThrowJsonException_MetadataValueWasNotString(reader.TokenType);
                }

                converter.CreateInstanceForReferenceResolver(ref reader, ref state, options);

                string referenceId = reader.GetString() !;
                state.ReferenceResolver.AddReference(referenceId, state.Current.ReturnValue !);

                // Need to Read $values property name.
                state.Current.ObjectState = StackFrameObjectState.ReadAheadValuesName;
            }

            // Clear the metadata property name that was set in case of failure on ResolverReference/AddReference.
            state.Current.JsonPropertyName = null;

            if (state.Current.ObjectState == StackFrameObjectState.ReadAheadRefEndObject)
            {
                if (!TryReadAheadMetadataAndSetState(ref reader, ref state, StackFrameObjectState.ReadRefEndObject))
                {
                    return(false);
                }
            }

            if (state.Current.ObjectState == StackFrameObjectState.ReadRefEndObject)
            {
                if (reader.TokenType != JsonTokenType.EndObject)
                {
                    // We just read a property. The only valid next tokens are EndObject and PropertyName.
                    Debug.Assert(reader.TokenType == JsonTokenType.PropertyName);

                    ThrowHelper.ThrowJsonException_MetadataReferenceObjectCannotContainOtherProperties(reader.GetSpan(), ref state);
                }

                return(true);
            }

            if (state.Current.ObjectState == StackFrameObjectState.ReadAheadValuesName)
            {
                if (!TryReadAheadMetadataAndSetState(ref reader, ref state, StackFrameObjectState.ReadValuesName))
                {
                    return(false);
                }
            }

            if (state.Current.ObjectState == StackFrameObjectState.ReadValuesName)
            {
                if (reader.TokenType != JsonTokenType.PropertyName)
                {
                    ThrowHelper.ThrowJsonException_MetadataPreservedArrayValuesNotFound(ref state, converter.TypeToConvert);
                }

                ReadOnlySpan <byte> propertyName = reader.GetSpan();

                if (GetMetadataPropertyName(propertyName) != MetadataPropertyName.Values)
                {
                    ThrowHelper.ThrowJsonException_MetadataPreservedArrayInvalidProperty(ref state, converter.TypeToConvert, reader);
                }

                // Remember the property in case we get an exception in one of the array elements.
                state.Current.JsonPropertyName = s_valuesPropertyName;

                state.Current.ObjectState = StackFrameObjectState.ReadAheadValuesStartArray;
            }

            if (state.Current.ObjectState == StackFrameObjectState.ReadAheadValuesStartArray)
            {
                if (!TryReadAheadMetadataAndSetState(ref reader, ref state, StackFrameObjectState.ReadValuesStartArray))
                {
                    return(false);
                }
            }

            if (state.Current.ObjectState == StackFrameObjectState.ReadValuesStartArray)
            {
                if (reader.TokenType != JsonTokenType.StartArray)
                {
                    ThrowHelper.ThrowJsonException_MetadataValuesInvalidToken(reader.TokenType);
                }
                state.Current.ValidateEndTokenOnArray = true;
                state.Current.ObjectState             = StackFrameObjectState.CreatedObject;
            }

            return(true);
        }
Beispiel #12
0
        internal sealed override bool OnTryWrite(
            Utf8JsonWriter writer,
            T value,
            JsonSerializerOptions options,
            ref WriteStack state)
        {
            JsonTypeInfo jsonTypeInfo = state.Current.JsonTypeInfo;

            object obj = value; // box once

            if (!state.SupportContinuation)
            {
                writer.WriteStartObject();
                if (options.ReferenceHandlingStrategy == ReferenceHandlingStrategy.Preserve)
                {
                    MetadataPropertyName propertyName = JsonSerializer.WriteReferenceForObject(this, ref state, writer);
                    Debug.Assert(propertyName != MetadataPropertyName.Ref);
                }

                if (obj is IJsonOnSerializing onSerializing)
                {
                    onSerializing.OnSerializing();
                }

                List <KeyValuePair <string, JsonPropertyInfo?> > properties = jsonTypeInfo.PropertyCache !.List;
                for (int i = 0; i < properties.Count; i++)
                {
                    JsonPropertyInfo jsonPropertyInfo = properties[i].Value !;
                    if (jsonPropertyInfo.ShouldSerialize)
                    {
                        // Remember the current property for JsonPath support if an exception is thrown.
                        state.Current.JsonPropertyInfo = jsonPropertyInfo;
                        state.Current.NumberHandling   = jsonPropertyInfo.NumberHandling;

                        bool success = jsonPropertyInfo.GetMemberAndWriteJson(obj, ref state, writer);
                        // Converters only return 'false' when out of data which is not possible in fast path.
                        Debug.Assert(success);

                        state.Current.EndProperty();
                    }
                }

                // Write extension data after the normal properties.
                JsonPropertyInfo?dataExtensionProperty = jsonTypeInfo.DataExtensionProperty;
                if (dataExtensionProperty?.ShouldSerialize == true)
                {
                    // Remember the current property for JsonPath support if an exception is thrown.
                    state.Current.JsonPropertyInfo = dataExtensionProperty;
                    state.Current.NumberHandling   = dataExtensionProperty.NumberHandling;

                    bool success = dataExtensionProperty.GetMemberAndWriteJsonExtensionData(obj, ref state, writer);
                    Debug.Assert(success);

                    state.Current.EndProperty();
                }

                writer.WriteEndObject();
            }
            else
            {
                if (!state.Current.ProcessedStartToken)
                {
                    writer.WriteStartObject();
                    if (options.ReferenceHandlingStrategy == ReferenceHandlingStrategy.Preserve)
                    {
                        MetadataPropertyName propertyName = JsonSerializer.WriteReferenceForObject(this, ref state, writer);
                        Debug.Assert(propertyName != MetadataPropertyName.Ref);
                    }

                    if (obj is IJsonOnSerializing onSerializing)
                    {
                        onSerializing.OnSerializing();
                    }

                    state.Current.ProcessedStartToken = true;
                }

                List <KeyValuePair <string, JsonPropertyInfo?> >?propertyList = jsonTypeInfo.PropertyCache !.List !;
                while (state.Current.EnumeratorIndex < propertyList.Count)
                {
                    JsonPropertyInfo?jsonPropertyInfo = propertyList ![state.Current.EnumeratorIndex].Value;