Пример #1
0
        public override int Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
        {
            if (reader.TokenType == JsonTokenType.String)
            {
                if (!int.TryParse(reader.GetString(), out var res))
                {
                    throw new JsonException("Failed to parse json number");
                }

                return(res);
            }

            return(reader.GetInt32());
        }
Пример #2
0
        public override int Read(ref System.Text.Json.Utf8JsonReader reader, System.Type typeToConvert, System.Text.Json.JsonSerializerOptions options)
        {
            if (reader.TokenType == JsonTokenType.String)
            {
                ReadOnlySpan <byte> span = reader.HasValueSequence ? reader.ValueSequence.ToArray() : reader.ValueSpan;
                if (Utf8Parser.TryParse(span, out int number, out int bytesConsumed) && span.Length == bytesConsumed)
                {
                    return(number);
                }

                if (int.TryParse(reader.GetString(), out number))
                {
                    return(number);
                }
            }

            return(reader.GetInt32());
        }
        internal static bool TryReadMetadata(JsonConverter converter, JsonTypeInfo jsonTypeInfo, ref Utf8JsonReader reader, ref ReadStack state)
        {
            Debug.Assert(state.Current.ObjectState == StackFrameObjectState.StartToken);
            Debug.Assert(state.Current.CanContainMetadata);

            while (true)
            {
                if (state.Current.PropertyState == StackFramePropertyState.None)
                {
                    state.Current.PropertyState = StackFramePropertyState.ReadName;

                    // Read the property name.
                    if (!reader.Read())
                    {
                        return(false);
                    }
                }

                if (state.Current.PropertyState < StackFramePropertyState.Name)
                {
                    if (reader.TokenType == JsonTokenType.EndObject)
                    {
                        // Read the entire object while parsing for metadata.
                        return(true);
                    }

                    // We just read a property. The only valid next tokens are EndObject and PropertyName.
                    Debug.Assert(reader.TokenType == JsonTokenType.PropertyName);

                    if (state.Current.MetadataPropertyNames.HasFlag(MetadataPropertyName.Ref))
                    {
                        // No properties whatsoever should follow a $ref property.
                        ThrowHelper.ThrowJsonException_MetadataReferenceObjectCannotContainOtherProperties(reader.GetSpan(), ref state);
                    }

                    ReadOnlySpan <byte> propertyName = reader.GetSpan();
                    switch (state.Current.LatestMetadataPropertyName = GetMetadataPropertyName(propertyName, jsonTypeInfo.PolymorphicTypeResolver))
                    {
                    case MetadataPropertyName.Id:
                        state.Current.JsonPropertyName = s_idPropertyName;

                        if (state.ReferenceResolver is null)
                        {
                            // Found an $id property in a type that doesn't support reference preservation
                            ThrowHelper.ThrowJsonException_MetadataUnexpectedProperty(propertyName, ref state);
                        }
                        if ((state.Current.MetadataPropertyNames & (MetadataPropertyName.Id | MetadataPropertyName.Ref)) != 0)
                        {
                            // No $id or $ref properties should precede $id properties.
                            ThrowHelper.ThrowJsonException_MetadataIdIsNotFirstProperty(propertyName, ref state);
                        }
                        if (!converter.CanHaveMetadata)
                        {
                            // Should not be permitted unless the converter is capable of handling metadata.
                            ThrowHelper.ThrowJsonException_MetadataCannotParsePreservedObjectIntoImmutable(converter.TypeToConvert);
                        }

                        break;

                    case MetadataPropertyName.Ref:
                        state.Current.JsonPropertyName = s_refPropertyName;

                        if (state.ReferenceResolver is null)
                        {
                            // Found a $ref property in a type that doesn't support reference preservation
                            ThrowHelper.ThrowJsonException_MetadataUnexpectedProperty(propertyName, ref state);
                        }
                        if (converter.IsValueType)
                        {
                            // Should not be permitted if the converter is a struct.
                            ThrowHelper.ThrowJsonException_MetadataInvalidReferenceToValueType(converter.TypeToConvert);
                        }
                        if (state.Current.MetadataPropertyNames != 0)
                        {
                            // No metadata properties should precede a $ref property.
                            ThrowHelper.ThrowJsonException_MetadataReferenceObjectCannotContainOtherProperties(reader.GetSpan(), ref state);
                        }

                        break;

                    case MetadataPropertyName.Type:
                        state.Current.JsonPropertyName = jsonTypeInfo.PolymorphicTypeResolver?.TypeDiscriminatorPropertyNameUtf8 ?? s_typePropertyName;

                        if (jsonTypeInfo.PolymorphicTypeResolver is null)
                        {
                            // Found a $type property in a type that doesn't support polymorphism
                            ThrowHelper.ThrowJsonException_MetadataUnexpectedProperty(propertyName, ref state);
                        }
                        if (state.PolymorphicTypeDiscriminator != null)
                        {
                            ThrowHelper.ThrowJsonException_MetadataDuplicateTypeProperty();
                        }

                        break;

                    case MetadataPropertyName.Values:
                        state.Current.JsonPropertyName = s_valuesPropertyName;

                        if (state.Current.MetadataPropertyNames == MetadataPropertyName.None)
                        {
                            // Cannot have a $values property unless there are preceding metadata properties.
                            ThrowHelper.ThrowJsonException_MetadataStandaloneValuesProperty(ref state, propertyName);
                        }

                        break;

                    default:
                        Debug.Assert(state.Current.LatestMetadataPropertyName == MetadataPropertyName.None);

                        // Encountered a non-metadata property, exit the reader.
                        return(true);
                    }

                    state.Current.PropertyState = StackFramePropertyState.Name;
                }

                if (state.Current.PropertyState < StackFramePropertyState.ReadValue)
                {
                    state.Current.PropertyState = StackFramePropertyState.ReadValue;

                    // Read the property value.
                    if (!reader.Read())
                    {
                        return(false);
                    }
                }

                Debug.Assert(state.Current.PropertyState == StackFramePropertyState.ReadValue);

                switch (state.Current.LatestMetadataPropertyName)
                {
                case MetadataPropertyName.Id:
                    if (reader.TokenType != JsonTokenType.String)
                    {
                        ThrowHelper.ThrowJsonException_MetadataValueWasNotString(reader.TokenType);
                    }

                    if (state.ReferenceId != null)
                    {
                        ThrowHelper.ThrowNotSupportedException_ObjectWithParameterizedCtorRefMetadataNotSupported(s_refPropertyName, ref reader, ref state);
                    }

                    state.ReferenceId = reader.GetString();
                    break;

                case MetadataPropertyName.Ref:
                    if (reader.TokenType != JsonTokenType.String)
                    {
                        ThrowHelper.ThrowJsonException_MetadataValueWasNotString(reader.TokenType);
                    }

                    if (state.ReferenceId != null)
                    {
                        ThrowHelper.ThrowNotSupportedException_ObjectWithParameterizedCtorRefMetadataNotSupported(s_refPropertyName, ref reader, ref state);
                    }

                    state.ReferenceId = reader.GetString();
                    break;

                case MetadataPropertyName.Type:
                    Debug.Assert(state.PolymorphicTypeDiscriminator == null);

                    switch (reader.TokenType)
                    {
                    case JsonTokenType.String:
                        state.PolymorphicTypeDiscriminator = reader.GetString();
                        break;

                    case JsonTokenType.Number:
                        state.PolymorphicTypeDiscriminator = reader.GetInt32();
                        break;

                    default:
                        ThrowHelper.ThrowJsonException_MetadataValueWasNotString(reader.TokenType);
                        break;
                    }

                    break;

                case MetadataPropertyName.Values:

                    if (reader.TokenType != JsonTokenType.StartArray)
                    {
                        ThrowHelper.ThrowJsonException_MetadataValuesInvalidToken(reader.TokenType);
                    }

                    state.Current.PropertyState          = StackFramePropertyState.None;
                    state.Current.MetadataPropertyNames |= state.Current.LatestMetadataPropertyName;
                    return(true);    // "$values" property contains the nested payload, exit the metadata reader now.

                default:
                    Debug.Fail("Non-metadata properties should not reach this stage.");
                    break;
                }

                state.Current.MetadataPropertyNames |= state.Current.LatestMetadataPropertyName;
                state.Current.PropertyState          = StackFramePropertyState.None;
                state.Current.JsonPropertyName       = null;
            }
        }