Esempio n. 1
0
        // If this method is changed, also change ApplyValueToEnumerable.
        internal static void ApplyObjectToEnumerable(
            object value,
            ref ReadStack state,
            bool setPropertyDirectly = false)
        {
            Debug.Assert(!state.Current.SkipProperty);

            if (state.Current.IsProcessingObject(ClassType.Enumerable))
            {
                if (state.Current.TempEnumerableValues != null)
                {
                    state.Current.TempEnumerableValues.Add(value);
                }
                else
                {
                    if (!(state.Current.ReturnValue is IList list))
                    {
                        ThrowHelper.ThrowJsonException_DeserializeUnableToConvertValue(value.GetType());
                        return;
                    }
                    list.Add(value);
                }
            }
            else if (!setPropertyDirectly && state.Current.IsProcessingProperty(ClassType.Enumerable))
            {
                Debug.Assert(state.Current.JsonPropertyInfo != null);
                Debug.Assert(state.Current.ReturnValue != null);
                if (state.Current.TempEnumerableValues != null)
                {
                    state.Current.TempEnumerableValues.Add(value);
                }
                else
                {
                    IList list = (IList)state.Current.JsonPropertyInfo.GetValueAsObject(state.Current.ReturnValue);
                    if (list == null ||
                        // ImmutableArray<T> is a struct, so default value won't be null.
                        state.Current.JsonPropertyInfo.RuntimePropertyType.FullName.StartsWith(DefaultImmutableEnumerableConverter.ImmutableArrayGenericTypeName))
                    {
                        state.Current.JsonPropertyInfo.SetValueAsObject(state.Current.ReturnValue, value);
                    }
                    else
                    {
                        list.Add(value);
                    }
                }
            }
            else if (state.Current.IsProcessingObject(ClassType.Dictionary) ||
                     (state.Current.IsProcessingProperty(ClassType.Dictionary) && !setPropertyDirectly))
            {
                Debug.Assert(state.Current.ReturnValue != null);
                IDictionary dictionary = (IDictionary)state.Current.JsonPropertyInfo.GetValueAsObject(state.Current.ReturnValue);

                string key = state.Current.KeyName;
                Debug.Assert(!string.IsNullOrEmpty(key));
                dictionary[key] = value;
            }
            else if (state.Current.IsProcessingObject(ClassType.IDictionaryConstructible) ||
                     (state.Current.IsProcessingProperty(ClassType.IDictionaryConstructible) && !setPropertyDirectly))
            {
                Debug.Assert(state.Current.TempDictionaryValues != null);
                IDictionary dictionary = (IDictionary)state.Current.TempDictionaryValues;

                string key = state.Current.KeyName;
                Debug.Assert(!string.IsNullOrEmpty(key));
                dictionary[key] = value;
            }
            else
            {
                Debug.Assert(state.Current.JsonPropertyInfo != null);
                state.Current.JsonPropertyInfo.SetValueAsObject(state.Current.ReturnValue, value);
            }
        }
Esempio n. 2
0
        private static void HandleStartDictionary(JsonSerializerOptions options, ref Utf8JsonReader reader, ref ReadStack state)
        {
            Debug.Assert(!state.Current.IsProcessingEnumerable);

            JsonPropertyInfo jsonPropertyInfo = state.Current.JsonPropertyInfo;

            if (jsonPropertyInfo == null)
            {
                jsonPropertyInfo = state.Current.JsonClassInfo.CreateRootObject(options);
            }

            Debug.Assert(jsonPropertyInfo != null);

            // A nested object or dictionary so push new frame.
            if (state.Current.CollectionPropertyInitialized)
            {
                state.Push();
                state.Current.JsonClassInfo = jsonPropertyInfo.ElementClassInfo;
                state.Current.InitializeJsonPropertyInfo();
                state.Current.CollectionPropertyInitialized = true;

                ClassType classType = state.Current.JsonClassInfo.ClassType;
                if (classType == ClassType.Value &&
                    jsonPropertyInfo.ElementClassInfo.Type != typeof(object) &&
                    jsonPropertyInfo.ElementClassInfo.Type != typeof(JsonElement))
                {
                    ThrowHelper.ThrowJsonException_DeserializeUnableToConvertValue(state.Current.JsonClassInfo.Type, reader, state.JsonPath());
                }

                JsonClassInfo classInfo = state.Current.JsonClassInfo;

                if (state.Current.IsProcessingIDictionaryConstructible)
                {
                    state.Current.TempDictionaryValues = (IDictionary)classInfo.CreateConcreteDictionary();
                }
                else
                {
                    if (classInfo.CreateObject == null)
                    {
                        ThrowHelper.ThrowJsonException_DeserializeUnableToConvertValue(classInfo.Type, reader, state.JsonPath());
                        return;
                    }
                    state.Current.ReturnValue = classInfo.CreateObject();
                }

                return;
            }

            state.Current.CollectionPropertyInitialized = true;

            if (state.Current.IsProcessingIDictionaryConstructible)
            {
                JsonClassInfo dictionaryClassInfo;
                if (jsonPropertyInfo.DeclaredPropertyType == jsonPropertyInfo.ImplementedPropertyType)
                {
                    dictionaryClassInfo = options.GetOrAddClass(jsonPropertyInfo.RuntimePropertyType);
                }
                else
                {
                    dictionaryClassInfo = options.GetOrAddClass(jsonPropertyInfo.DeclaredPropertyType);
                }

                state.Current.TempDictionaryValues = (IDictionary)dictionaryClassInfo.CreateConcreteDictionary();
            }
            else
            {
                // Create the dictionary.
                JsonClassInfo dictionaryClassInfo = jsonPropertyInfo.RuntimeClassInfo;
                IDictionary   value = (IDictionary)dictionaryClassInfo.CreateObject();

                if (value != null)
                {
                    if (state.Current.ReturnValue != null)
                    {
                        state.Current.JsonPropertyInfo.SetValueAsObject(state.Current.ReturnValue, value);
                    }
                    else
                    {
                        // A dictionary is being returned directly, or a nested dictionary.
                        state.Current.SetReturnValue(value);
                    }
                }
            }
        }
Esempio n. 3
0
        private static void ReadCore(
            JsonSerializerOptions options,
            ref Utf8JsonReader reader,
            ref ReadStack readStack)
        {
            try
            {
                JsonReaderState initialState = default;

                while (true)
                {
                    if (readStack.ReadAhead)
                    {
                        // When we're reading ahead we always have to save the state
                        // as we don't know if the next token is an opening object or
                        // array brace.
                        initialState = reader.CurrentState;
                    }

                    if (!reader.Read())
                    {
                        // Need more data
                        break;
                    }

                    JsonTokenType tokenType = reader.TokenType;

                    if (JsonHelpers.IsInRangeInclusive(tokenType, JsonTokenType.String, JsonTokenType.False))
                    {
                        Debug.Assert(tokenType == JsonTokenType.String || tokenType == JsonTokenType.Number || tokenType == JsonTokenType.True || tokenType == JsonTokenType.False);

                        HandleValue(tokenType, options, ref reader, ref readStack);
                    }
                    else if (tokenType == JsonTokenType.PropertyName)
                    {
                        HandlePropertyName(options, ref reader, ref readStack);
                    }
                    else if (tokenType == JsonTokenType.StartObject)
                    {
                        if (readStack.Current.SkipProperty)
                        {
                            readStack.Push();
                            readStack.Current.Drain = true;
                        }
                        else if (readStack.Current.IsProcessingValue)
                        {
                            if (!HandleObjectAsValue(tokenType, options, ref reader, ref readStack, ref initialState))
                            {
                                // Need more data
                                break;
                            }
                        }
                        else if (readStack.Current.IsProcessingDictionary || readStack.Current.IsProcessingIDictionaryConstructibleOrKeyValuePair)
                        {
                            HandleStartDictionary(options, ref reader, ref readStack);
                        }
                        else
                        {
                            HandleStartObject(options, ref reader, ref readStack);
                        }
                    }
                    else if (tokenType == JsonTokenType.EndObject)
                    {
                        if (readStack.Current.Drain)
                        {
                            readStack.Pop();
                        }
                        else if (readStack.Current.IsProcessingDictionary || readStack.Current.IsProcessingIDictionaryConstructibleOrKeyValuePair)
                        {
                            HandleEndDictionary(options, ref reader, ref readStack);
                        }
                        else
                        {
                            HandleEndObject(options, ref reader, ref readStack);
                        }
                    }
                    else if (tokenType == JsonTokenType.StartArray)
                    {
                        if (!readStack.Current.IsProcessingValue)
                        {
                            HandleStartArray(options, ref reader, ref readStack);
                        }
                        else if (!HandleObjectAsValue(tokenType, options, ref reader, ref readStack, ref initialState))
                        {
                            // Need more data
                            break;
                        }
                    }
                    else if (tokenType == JsonTokenType.EndArray)
                    {
                        HandleEndArray(options, ref reader, ref readStack);
                    }
                    else if (tokenType == JsonTokenType.Null)
                    {
                        HandleNull(ref reader, ref readStack, options);
                    }
                }
            }
            catch (JsonReaderException e)
            {
                // Re-throw with Path information.
                ThrowHelper.ReThrowWithPath(e, readStack.JsonPath);
            }

            readStack.BytesConsumed += reader.BytesConsumed;
            return;
        }
Esempio n. 4
0
 protected abstract void OnReadEnumerable(JsonTokenType tokenType, ref ReadStack state, ref Utf8JsonReader reader);
        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;
                }

                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 (reader._stringHasEscaping)
                {
                    int idx = propertyName.IndexOf(JsonConstants.BackSlash);
                    Debug.Assert(idx != -1);
                    propertyName = GetUnescapedString(propertyName, idx);
                }

                JsonPropertyInfo jsonPropertyInfo = state.Current.JsonClassInfo.GetProperty(propertyName, ref state.Current);
                if (jsonPropertyInfo == JsonPropertyInfo.s_missingProperty)
                {
                    JsonPropertyInfo?dataExtProperty = state.Current.JsonClassInfo !.DataExtensionProperty;
                    if (dataExtProperty == null)
                    {
                        state.Current.JsonPropertyInfo = JsonPropertyInfo.s_missingProperty;
                    }
                    else
                    {
                        state.Current.JsonPropertyInfo = dataExtProperty;
                        state.Current.JsonPropertyName = propertyName.ToArray();
                        state.Current.KeyName          = JsonHelpers.Utf8GetString(propertyName);
                        state.Current.CollectionPropertyInitialized = true;

                        CreateDataExtensionProperty(dataExtProperty, ref state);
                    }
                }
                else
                {
                    // Support JsonException.Path.
                    Debug.Assert(
                        jsonPropertyInfo.JsonPropertyName == null ||
                        options.PropertyNameCaseInsensitive ||
                        propertyName.SequenceEqual(jsonPropertyInfo.JsonPropertyName));

                    state.Current.JsonPropertyInfo = jsonPropertyInfo;

                    if (jsonPropertyInfo.JsonPropertyName == null)
                    {
                        byte[] propertyNameArray = propertyName.ToArray();
                        if (options.PropertyNameCaseInsensitive)
                        {
                            // Each payload can have a different name here; remember the value on the temporary stack.
                            state.Current.JsonPropertyName = propertyNameArray;
                        }
                        else
                        {
                            // Prevent future allocs by caching globally on the JsonPropertyInfo which is specific to a Type+PropertyName
                            // so it will match the incoming payload except when case insensitivity is enabled (which is handled above).
                            state.Current.JsonPropertyInfo.JsonPropertyName = propertyNameArray;
                        }
                    }
                }

                // Increment the PropertyIndex so JsonClassInfo.GetProperty() starts with the next property.
                state.Current.PropertyIndex++;
            }
        }
Esempio n. 6
0
        private static void HandleStartArray(
            JsonSerializerOptions options,
            ref Utf8JsonReader reader,
            ref ReadStack state)
        {
            JsonPropertyInfo jsonPropertyInfo = state.Current.JsonPropertyInfo;

            if (state.Current.SkipProperty)
            {
                // The array is not being applied to the object.
                state.Push();
                state.Current.Drain = true;
                return;
            }

            if (jsonPropertyInfo == null)
            {
                jsonPropertyInfo = state.Current.JsonClassInfo.CreateRootObject(options);
            }
            else if (state.Current.JsonClassInfo.ClassType == ClassType.Unknown)
            {
                jsonPropertyInfo = state.Current.JsonClassInfo.CreatePolymorphicProperty(jsonPropertyInfo, typeof(object), options);
            }

            // Verify that we don't have a multidimensional array.
            Type arrayType = jsonPropertyInfo.RuntimePropertyType;

            if (!typeof(IEnumerable).IsAssignableFrom(arrayType) || (arrayType.IsArray && arrayType.GetArrayRank() > 1))
            {
                ThrowHelper.ThrowJsonException_DeserializeUnableToConvertValue(arrayType, reader, state.JsonPath);
            }

            Debug.Assert(state.Current.IsProcessingEnumerableOrDictionary);

            if (state.Current.CollectionPropertyInitialized)
            {
                // A nested json array so push a new stack frame.
                Type elementType = jsonPropertyInfo.ElementClassInfo.Type;

                state.Push();
                state.Current.Initialize(elementType, options);
            }

            state.Current.CollectionPropertyInitialized = true;
            jsonPropertyInfo = state.Current.JsonPropertyInfo;

            if (state.Current.JsonClassInfo.ClassType == ClassType.Value)
            {
                state.Current.JsonPropertyInfo.Read(JsonTokenType.StartObject, ref state, ref reader);
            }
            else
            {
                // If current property is already set (from a constructor, for example) leave as-is.
                if (jsonPropertyInfo.GetValueAsObject(state.Current.ReturnValue) == null)
                {
                    // Create the enumerable.
                    object value = ReadStackFrame.CreateEnumerableValue(ref reader, ref state, options);

                    // If value is not null, then we don't have a converter so apply the value.
                    if (value != null)
                    {
                        if (state.Current.ReturnValue != null)
                        {
                            state.Current.JsonPropertyInfo.SetValueAsObject(state.Current.ReturnValue, value);
                        }
                        else
                        {
                            // Primitive arrays being returned without object
                            state.Current.SetReturnValue(value);
                        }
                    }
                }
            }
        }
Esempio n. 7
0
        // If this method is changed, also change ApplyObjectToEnumerable.
        internal static void ApplyValueToEnumerable <TProperty>(
            ref TProperty value,
            ref ReadStack state,
            ref Utf8JsonReader reader)
        {
            Debug.Assert(!state.Current.SkipProperty);

            if (state.Current.IsEnumerable)
            {
                if (state.Current.TempEnumerableValues != null)
                {
                    ((IList <TProperty>)state.Current.TempEnumerableValues).Add(value);
                }
                else
                {
                    ((IList <TProperty>)state.Current.ReturnValue).Add(value);
                }
            }
            else if (state.Current.IsEnumerableProperty)
            {
                Debug.Assert(state.Current.JsonPropertyInfo != null);
                Debug.Assert(state.Current.ReturnValue != null);
                if (state.Current.TempEnumerableValues != null)
                {
                    ((IList <TProperty>)state.Current.TempEnumerableValues).Add(value);
                }
                else
                {
                    IList <TProperty> list = (IList <TProperty>)state.Current.JsonPropertyInfo.GetValueAsObject(state.Current.ReturnValue);
                    if (list == null)
                    {
                        state.Current.JsonPropertyInfo.SetValueAsObject(state.Current.ReturnValue, value);
                    }
                    else
                    {
                        list.Add(value);
                    }
                }
            }
            else if (state.Current.IsProcessingDictionary)
            {
                Debug.Assert(state.Current.ReturnValue != null);
                IDictionary <string, TProperty> dictionary = (IDictionary <string, TProperty>)state.Current.JsonPropertyInfo.GetValueAsObject(state.Current.ReturnValue);

                string key = state.Current.KeyName;
                Debug.Assert(!string.IsNullOrEmpty(key));
                dictionary[key] = value;
            }
            else if (state.Current.IsProcessingIDictionaryConstructible)
            {
                Debug.Assert(state.Current.TempDictionaryValues != null);
                IDictionary <string, TProperty> dictionary = (IDictionary <string, TProperty>)state.Current.TempDictionaryValues;

                string key = state.Current.KeyName;
                Debug.Assert(!string.IsNullOrEmpty(key));
                dictionary[key] = value;
            }
            else
            {
                Debug.Assert(state.Current.JsonPropertyInfo != null);
                state.Current.JsonPropertyInfo.SetValueAsObject(state.Current.ReturnValue, value);
            }
        }
Esempio n. 8
0
        internal static JsonPropertyInfo LookupProperty(
            object obj,
            ref Utf8JsonReader reader,
            JsonSerializerOptions options,
            ref ReadStack state,
            out bool useExtensionProperty,
            bool createExtensionProperty = true)
        {
            Debug.Assert(state.Current.JsonClassInfo.ClassType == ClassType.Object);

            ReadOnlySpan <byte> unescapedPropertyName = GetPropertyName(ref state, ref reader, options);

            JsonPropertyInfo jsonPropertyInfo = state.Current.JsonClassInfo.GetProperty(unescapedPropertyName, ref state.Current);

            // Increment PropertyIndex so GetProperty() starts with the next property the next time this function is called.
            state.Current.PropertyIndex++;

            // Determine if we should use the extension property.
            if (jsonPropertyInfo == JsonPropertyInfo.s_missingProperty)
            {
                JsonPropertyInfo?dataExtProperty = state.Current.JsonClassInfo.DataExtensionProperty;
                if (dataExtProperty != null)
                {
                    state.Current.JsonPropertyNameAsString = JsonHelpers.Utf8GetString(unescapedPropertyName);

                    if (createExtensionProperty)
                    {
                        CreateDataExtensionProperty(obj, dataExtProperty);
                    }

                    jsonPropertyInfo     = dataExtProperty;
                    useExtensionProperty = true;
                }
                else
                {
                    useExtensionProperty = false;
                }

                state.Current.JsonPropertyInfo = jsonPropertyInfo;
                return(jsonPropertyInfo);
            }

            // Support JsonException.Path.
            Debug.Assert(
                jsonPropertyInfo.JsonPropertyName == null ||
                options.PropertyNameCaseInsensitive ||
                unescapedPropertyName.SequenceEqual(jsonPropertyInfo.JsonPropertyName));

            state.Current.JsonPropertyInfo = jsonPropertyInfo;

            if (jsonPropertyInfo.JsonPropertyName == null)
            {
                byte[] propertyNameArray = unescapedPropertyName.ToArray();
                if (options.PropertyNameCaseInsensitive)
                {
                    // Each payload can have a different name here; remember the value on the temporary stack.
                    state.Current.JsonPropertyName = propertyNameArray;
                }
                else
                {
                    // Prevent future allocs by caching globally on the JsonPropertyInfo which is specific to a Type+PropertyName
                    // so it will match the incoming payload except when case insensitivity is enabled (which is handled above).
                    state.Current.JsonPropertyInfo.JsonPropertyName = propertyNameArray;
                }
            }

            state.Current.JsonPropertyInfo = jsonPropertyInfo;
            useExtensionProperty           = false;
            return(jsonPropertyInfo);
        }
        private static bool HandleNull(ref Utf8JsonReader reader, ref ReadStack state)
        {
            if (state.Current.SkipProperty)
            {
                return(false);
            }

            JsonPropertyInfo jsonPropertyInfo = state.Current.JsonPropertyInfo;

            if (jsonPropertyInfo == null || (reader.CurrentDepth == 0 && jsonPropertyInfo.CanBeNull))
            {
                Debug.Assert(state.IsLastFrame);
                Debug.Assert(state.Current.ReturnValue == null);
                return(true);
            }

            Debug.Assert(jsonPropertyInfo != null);

            if (state.Current.IsCollectionForClass)
            {
                AddNullToCollection(jsonPropertyInfo, ref reader, ref state);
                return(false);
            }

            if (state.Current.IsCollectionForProperty)
            {
                if (state.Current.CollectionPropertyInitialized)
                {
                    // Add the element.
                    AddNullToCollection(jsonPropertyInfo, ref reader, ref state);
                }
                else
                {
                    // Set the property to null.
                    ApplyObjectToEnumerable(null, ref state, ref reader, setPropertyDirectly: true);

                    // Reset so that `Is*Property` no longer returns true
                    state.Current.EndProperty();
                }

                return(false);
            }

            if (!jsonPropertyInfo.CanBeNull)
            {
                // Allow a value type converter to return a null value representation, such as JsonElement.
                // Most likely this will throw JsonException.
                jsonPropertyInfo.Read(JsonTokenType.Null, ref state, ref reader);
                return(false);
            }

            if (state.Current.ReturnValue == null)
            {
                Debug.Assert(state.IsLastFrame);
                return(true);
            }

            if (!jsonPropertyInfo.IgnoreNullValues)
            {
                state.Current.JsonPropertyInfo.SetValueAsObject(state.Current.ReturnValue, value: null);
            }

            return(false);
        }
Esempio n. 10
0
        private static async ValueTask <TValue> ReadAsync <TValue>(
            Stream utf8Json,
            Type returnType,
            JsonSerializerOptions options       = null,
            CancellationToken cancellationToken = default)
        {
            if (options == null)
            {
                options = JsonSerializerOptions.s_defaultOptions;
            }

            ReadStack readStack = default;

            readStack.Current.Initialize(returnType, options);

            var readerState = new JsonReaderState(options.GetReaderOptions());

            // todo: switch to ArrayBuffer implementation to handle and simplify the allocs?
            int utf8BomLength = JsonConstants.Utf8Bom.Length;

            byte[] buffer = ArrayPool <byte> .Shared.Rent(Math.Max(options.DefaultBufferSize, utf8BomLength));

            int  bytesInBuffer  = 0;
            long totalBytesRead = 0;
            int  clearMax       = 0;
            bool firstIteration = true;

            try
            {
                while (true)
                {
                    // Read from the stream until either our buffer is filled or we hit EOF.
                    // Calling ReadCore is relatively expensive, so we minimize the number of times
                    // we need to call it.
                    bool isFinalBlock = false;
                    while (true)
                    {
                        int bytesRead = await utf8Json.ReadAsync(
#if BUILDING_INBOX_LIBRARY
                            buffer.AsMemory(bytesInBuffer),
#else
                            buffer, bytesInBuffer, buffer.Length - bytesInBuffer,
#endif
                            cancellationToken).ConfigureAwait(false);

                        if (bytesRead == 0)
                        {
                            isFinalBlock = true;
                            break;
                        }

                        totalBytesRead += bytesRead;
                        bytesInBuffer  += bytesRead;

                        if (bytesInBuffer == buffer.Length)
                        {
                            break;
                        }
                    }

                    if (bytesInBuffer > clearMax)
                    {
                        clearMax = bytesInBuffer;
                    }

                    int start = 0;
                    if (firstIteration)
                    {
                        firstIteration = false;
                        // Handle the UTF-8 BOM if present
                        Debug.Assert(buffer.Length >= JsonConstants.Utf8Bom.Length);
                        if (buffer.AsSpan().StartsWith(JsonConstants.Utf8Bom))
                        {
                            start         += utf8BomLength;
                            bytesInBuffer -= utf8BomLength;
                        }
                    }

                    // Process the data available
                    ReadCore(
                        ref readerState,
                        isFinalBlock,
                        new ReadOnlySpan <byte>(buffer, start, bytesInBuffer),
                        options,
                        ref readStack);

                    Debug.Assert(readStack.BytesConsumed <= bytesInBuffer);
                    int bytesConsumed = checked ((int)readStack.BytesConsumed);

                    bytesInBuffer -= bytesConsumed;

                    if (isFinalBlock)
                    {
                        break;
                    }

                    // Check if we need to shift or expand the buffer because there wasn't enough data to complete deserialization.
                    if ((uint)bytesInBuffer > ((uint)buffer.Length / 2))
                    {
                        // We have less than half the buffer available, double the buffer size.
                        byte[] dest = ArrayPool <byte> .Shared.Rent((buffer.Length < (int.MaxValue / 2))?buffer.Length * 2 : int.MaxValue);

                        // Copy the unprocessed data to the new buffer while shifting the processed bytes.
                        Buffer.BlockCopy(buffer, bytesConsumed + start, dest, 0, bytesInBuffer);

                        new Span <byte>(buffer, 0, clearMax).Clear();
                        ArrayPool <byte> .Shared.Return(buffer);

                        clearMax = bytesInBuffer;
                        buffer   = dest;
                    }
                    else if (bytesInBuffer != 0)
                    {
                        // Shift the processed bytes to the beginning of buffer to make more room.
                        Buffer.BlockCopy(buffer, bytesConsumed, buffer, 0, bytesInBuffer);
                    }
                }
            }
            finally
            {
                // Clear only what we used and return the buffer to the pool
                new Span <byte>(buffer, 0, clearMax).Clear();
                ArrayPool <byte> .Shared.Return(buffer);
            }

            if (bytesInBuffer != 0)
            {
                ThrowHelper.ThrowJsonException_DeserializeDataRemaining(totalBytesRead, bytesInBuffer);
            }

            return((TValue)readStack.Current.ReturnValue);
        }
Esempio n. 11
0
        private void VerifyRead(JsonTokenType tokenType, int depth, long bytesConsumed, ref ReadStack state, ref Utf8JsonReader reader)
        {
            switch (tokenType)
            {
            case JsonTokenType.StartArray:
                if (reader.TokenType != JsonTokenType.EndArray)
                {
                    ThrowHelper.ThrowJsonException_SerializationConverterRead(reader, state.JsonPath, ConverterBase.ToString());
                }
                else if (depth != reader.CurrentDepth)
                {
                    ThrowHelper.ThrowJsonException_SerializationConverterRead(reader, state.JsonPath, ConverterBase.ToString());
                }

                // Should not be possible to have not read anything.
                Debug.Assert(bytesConsumed < reader.BytesConsumed);
                break;

            case JsonTokenType.StartObject:
                if (reader.TokenType != JsonTokenType.EndObject)
                {
                    ThrowHelper.ThrowJsonException_SerializationConverterRead(reader, state.JsonPath, ConverterBase.ToString());
                }
                else if (depth != reader.CurrentDepth)
                {
                    ThrowHelper.ThrowJsonException_SerializationConverterRead(reader, state.JsonPath, ConverterBase.ToString());
                }

                // Should not be possible to have not read anything.
                Debug.Assert(bytesConsumed < reader.BytesConsumed);
                break;

            default:
                // Reading a single property value.
                if (reader.BytesConsumed != bytesConsumed)
                {
                    ThrowHelper.ThrowJsonException_SerializationConverterRead(reader, state.JsonPath, ConverterBase.ToString());
                }

                // Should not be possible to change token type.
                Debug.Assert(reader.TokenType == tokenType);

                break;
            }
        }
Esempio n. 12
0
        private static void HandleEndDictionary(JsonSerializerOptions options, ref Utf8JsonReader reader, ref ReadStack state)
        {
            if (state.Current.IsDictionaryProperty)
            {
                // We added the items to the dictionary already.
                state.Current.EndProperty();
            }
            else if (state.Current.IsIDictionaryConstructibleProperty)
            {
                Debug.Assert(state.Current.TempDictionaryValues != null);
                JsonDictionaryConverter converter = state.Current.JsonPropertyInfo.DictionaryConverter;
                state.Current.JsonPropertyInfo.SetValueAsObject(state.Current.ReturnValue, converter.CreateFromDictionary(ref state, state.Current.TempDictionaryValues, options));
                state.Current.EndProperty();
            }
            else
            {
                object value;
                if (state.Current.TempDictionaryValues != null)
                {
                    JsonDictionaryConverter converter = state.Current.JsonPropertyInfo.DictionaryConverter;
                    value = converter.CreateFromDictionary(ref state, state.Current.TempDictionaryValues, options);
                }
                else
                {
                    value = state.Current.ReturnValue;
                }

                if (state.IsLastFrame)
                {
                    // Set the return value directly since this will be returned to the user.
                    state.Current.Reset();
                    state.Current.ReturnValue = value;
                }
                else
                {
                    state.Pop();
                    ApplyObjectToEnumerable(value, ref state, ref reader);
                }
            }
        }
Esempio n. 13
0
        /// <summary>
        /// Internal version that allows re-entry with preserving ReadStack so that JsonPath works correctly.
        /// </summary>
        internal static TValue Deserialize <TValue>(ref Utf8JsonReader reader, JsonSerializerOptions options, ref ReadStack state, string?propertyName = null)
        {
            if (options == null)
            {
                throw new ArgumentNullException(nameof(options));
            }

            state.Current.InitializeReEntry(typeof(TValue), options, propertyName);

            JsonPropertyInfo jsonPropertyInfo = state.Current.JsonPropertyInfo !;

            JsonConverter <TValue> converter = (JsonConverter <TValue>)jsonPropertyInfo.ConverterBase;
            bool success = converter.TryRead(ref reader, jsonPropertyInfo.RuntimePropertyType !, options, ref state, out TValue value);

            Debug.Assert(success);

            // Clear the current property state since we are done processing it.
            state.Current.EndProperty();

            return(value);
        }
Esempio n. 14
0
        // If this method is changed, also change ApplyObjectToEnumerable.
        internal static void ApplyValueToEnumerable <TProperty>(
            ref TProperty value,
            ref ReadStack state)
        {
            Debug.Assert(!state.Current.SkipProperty);

            if (state.Current.IsProcessingObject(ClassType.Enumerable))
            {
                if (state.Current.TempEnumerableValues != null)
                {
                    ((IList <TProperty>)state.Current.TempEnumerableValues).Add(value);
                }
                else
                {
                    ((IList <TProperty>)state.Current.ReturnValue).Add(value);
                }
            }
            else if (state.Current.IsProcessingProperty(ClassType.Enumerable))
            {
                Debug.Assert(state.Current.JsonPropertyInfo != null);
                Debug.Assert(state.Current.ReturnValue != null);
                if (state.Current.TempEnumerableValues != null)
                {
                    ((IList <TProperty>)state.Current.TempEnumerableValues).Add(value);
                }
                else
                {
                    IList <TProperty> list = (IList <TProperty>)state.Current.JsonPropertyInfo.GetValueAsObject(state.Current.ReturnValue);
                    if (list == null)
                    {
                        state.Current.JsonPropertyInfo.SetValueAsObject(state.Current.ReturnValue, value);
                    }
                    else
                    {
                        list.Add(value);
                    }
                }
            }
            else if (state.Current.IsProcessingDictionary())
            {
                Debug.Assert(state.Current.ReturnValue != null);

                object currentDictionary = state.Current.JsonPropertyInfo.GetValueAsObject(state.Current.ReturnValue);

                string key = state.Current.KeyName;
                Debug.Assert(!string.IsNullOrEmpty(key));

                if (currentDictionary is IDictionary <string, TProperty> genericDict)
                {
                    Debug.Assert(!genericDict.IsReadOnly);
                    genericDict[key] = value;
                }
                else if (currentDictionary is IDictionary dict)
                {
                    Debug.Assert(!dict.IsReadOnly);
                    dict[key] = value;
                }
                else
                {
                    throw ThrowHelper.GetNotSupportedException_SerializationNotSupportedCollection(currentDictionary.GetType(), parentType: null, memberInfo: null);
                }
            }
            else if (state.Current.IsProcessingIDictionaryConstructible())
            {
                Debug.Assert(state.Current.TempDictionaryValues != null);
                IDictionary <string, TProperty> dictionary = (IDictionary <string, TProperty>)state.Current.TempDictionaryValues;

                string key = state.Current.KeyName;
                Debug.Assert(!string.IsNullOrEmpty(key));
                dictionary[key] = value;
            }
            else
            {
                Debug.Assert(state.Current.JsonPropertyInfo != null);
                state.Current.JsonPropertyInfo.SetValueAsObject(state.Current.ReturnValue, value);
            }
        }
Esempio n. 15
0
        private static void ReadValueCore(JsonSerializerOptions options, ref Utf8JsonReader reader, ref ReadStack readStack)
        {
            JsonReaderState state = reader.CurrentState;

            CheckSupportedOptions(state.Options, nameof(reader));

            // Value copy to overwrite the ref on an exception and undo the destructive reads.
            Utf8JsonReader restore = reader;

            ReadOnlySpan <byte>     valueSpan     = default;
            ReadOnlySequence <byte> valueSequence = default;

            try
            {
                switch (reader.TokenType)
                {
                // A new reader was created and has never been read,
                // so we need to move to the first token.
                // (or a reader has terminated and we're about to throw)
                case JsonTokenType.None:
                // Using a reader loop the caller has identified a property they wish to
                // hydrate into a JsonDocument. Move to the value first.
                case JsonTokenType.PropertyName:
                {
                    if (!reader.Read())
                    {
                        ThrowHelper.ThrowJsonReaderException(ref reader, ExceptionResource.ExpectedOneCompleteToken);
                    }
                    break;
                }
                }

                switch (reader.TokenType)
                {
                // Any of the "value start" states are acceptable.
                case JsonTokenType.StartObject:
                case JsonTokenType.StartArray:
                {
                    long startingOffset = reader.TokenStartIndex;

                    if (!reader.TrySkip())
                    {
                        ThrowHelper.ThrowJsonReaderException(ref reader, ExceptionResource.NotEnoughData);
                    }

                    long totalLength = reader.BytesConsumed - startingOffset;
                    ReadOnlySequence <byte> sequence = reader.OriginalSequence;

                    if (sequence.IsEmpty)
                    {
                        valueSpan = reader.OriginalSpan.Slice(
                            checked ((int)startingOffset),
                            checked ((int)totalLength));
                    }
                    else
                    {
                        valueSequence = sequence.Slice(startingOffset, totalLength);
                    }

                    Debug.Assert(
                        reader.TokenType == JsonTokenType.EndObject ||
                        reader.TokenType == JsonTokenType.EndArray);

                    break;
                }

                // Single-token values
                case JsonTokenType.Number:
                case JsonTokenType.True:
                case JsonTokenType.False:
                case JsonTokenType.Null:
                {
                    if (reader.HasValueSequence)
                    {
                        valueSequence = reader.ValueSequence;
                    }
                    else
                    {
                        valueSpan = reader.ValueSpan;
                    }

                    break;
                }

                // String's ValueSequence/ValueSpan omits the quotes, we need them back.
                case JsonTokenType.String:
                {
                    ReadOnlySequence <byte> sequence = reader.OriginalSequence;

                    if (sequence.IsEmpty)
                    {
                        // Since the quoted string fit in a ReadOnlySpan originally
                        // the contents length plus the two quotes can't overflow.
                        int payloadLength = reader.ValueSpan.Length + 2;
                        Debug.Assert(payloadLength > 1);

                        ReadOnlySpan <byte> readerSpan = reader.OriginalSpan;

                        Debug.Assert(
                            readerSpan[(int)reader.TokenStartIndex] == (byte)'"',
                            $"Calculated span starts with {readerSpan[(int)reader.TokenStartIndex]}");

                        Debug.Assert(
                            readerSpan[(int)reader.TokenStartIndex + payloadLength - 1] == (byte)'"',
                            $"Calculated span ends with {readerSpan[(int)reader.TokenStartIndex + payloadLength - 1]}");

                        valueSpan = readerSpan.Slice((int)reader.TokenStartIndex, payloadLength);
                    }
                    else
                    {
                        long payloadLength = 2;

                        if (reader.HasValueSequence)
                        {
                            payloadLength += reader.ValueSequence.Length;
                        }
                        else
                        {
                            payloadLength += reader.ValueSpan.Length;
                        }

                        valueSequence = sequence.Slice(reader.TokenStartIndex, payloadLength);
                        Debug.Assert(
                            valueSequence.First.Span[0] == (byte)'"',
                            $"Calculated sequence starts with {valueSequence.First.Span[0]}");

                        Debug.Assert(
                            valueSequence.ToArray()[payloadLength - 1] == (byte)'"',
                            $"Calculated sequence ends with {valueSequence.ToArray()[payloadLength - 1]}");
                    }

                    break;
                }

                default:
                {
                    byte displayByte;

                    if (reader.HasValueSequence)
                    {
                        displayByte = reader.ValueSequence.First.Span[0];
                    }
                    else
                    {
                        displayByte = reader.ValueSpan[0];
                    }

                    ThrowHelper.ThrowJsonReaderException(
                        ref reader,
                        ExceptionResource.ExpectedStartOfValueNotFound,
                        displayByte);

                    break;
                }
                }
            }
            catch (JsonReaderException ex)
            {
                reader = restore;
                // Re-throw with Path information.
                ThrowHelper.ReThrowWithPath(readStack, ex);
            }

            int length = valueSpan.IsEmpty ? checked ((int)valueSequence.Length) : valueSpan.Length;

            byte[] rented = ArrayPool <byte> .Shared.Rent(length);

            Span <byte> rentedSpan = rented.AsSpan(0, length);

            try
            {
                if (valueSpan.IsEmpty)
                {
                    valueSequence.CopyTo(rentedSpan);
                }
                else
                {
                    valueSpan.CopyTo(rentedSpan);
                }

                var newReader = new Utf8JsonReader(rentedSpan, isFinalBlock: true, state: default);

                ReadCore(options, ref newReader, ref readStack);

                if (newReader.BytesConsumed != length)
                {
                    ThrowHelper.ThrowJsonException_DeserializeDataRemaining(length, length - newReader.BytesConsumed);
                }
            }
            catch (JsonException)
            {
                reader = restore;
                throw;
            }
            finally
            {
                rentedSpan.Clear();
                ArrayPool <byte> .Shared.Return(rented);
            }
        }
        private static bool HandleNull(JsonSerializerOptions options, ref Utf8JsonReader reader, ref ReadStack state)
        {
            if (state.Current.SkipProperty)
            {
                // Clear the current property in case it is a dictionary, since dictionaries must have EndProperty() called when completed.
                // A non-dictionary property can also have EndProperty() called when completed, although it is redundant.
                state.Current.EndProperty();

                return(false);
            }

            JsonPropertyInfo?jsonPropertyInfo = state.Current.JsonPropertyInfo;

            if (jsonPropertyInfo == null || (reader.CurrentDepth == 0 && jsonPropertyInfo.CanBeNull))
            {
                Debug.Assert(state.IsLastFrame);
                Debug.Assert(state.Current.ReturnValue == null);
                return(true);
            }

            if (state.Current.IsProcessingCollectionObject())
            {
                AddNullToCollection(jsonPropertyInfo, ref reader, ref state);
                return(false);
            }

            if (state.Current.IsProcessingCollectionProperty())
            {
                if (state.Current.CollectionPropertyInitialized)
                {
                    // Add the element.
                    AddNullToCollection(jsonPropertyInfo, ref reader, ref state);
                }
                else
                {
                    // Set the property to null.
                    ApplyObjectToEnumerable(null, ref state, setPropertyDirectly: true);

                    // Reset so that `Is*Property` no longer returns true
                    state.Current.EndProperty();
                }

                return(false);
            }

            if (!jsonPropertyInfo.CanBeNull)
            {
                // Allow a value type converter to return a null value representation, such as JsonElement.
                // Most likely this will throw JsonException.
                jsonPropertyInfo.Read(JsonTokenType.Null, ref state, ref reader);
                return(false);
            }

            if (state.Current.ReturnValue == null)
            {
                Debug.Assert(state.IsLastFrame);
                return(true);
            }

            if (!jsonPropertyInfo.IgnoreNullValues)
            {
                jsonPropertyInfo.SetValueAsObject(state.Current.ReturnValue, value: null);
            }

            return(false);
        }
Esempio n. 17
0
        private static void ReadCore(
            JsonSerializerOptions options,
            ref Utf8JsonReader reader,
            ref ReadStack state)
        {
            try
            {
                JsonPropertyInfo jsonPropertyInfo = state.Current.JsonClassInfo !.PolicyProperty !;
                JsonConverter    converter        = jsonPropertyInfo.ConverterBase;

                if (!state.IsContinuation)
                {
                    if (!JsonConverter.SingleValueReadWithReadAhead(converter.ClassType, ref reader, ref state))
                    {
                        // Read more data until we have the full element.
                        state.BytesConsumed += reader.BytesConsumed;
                        return;
                    }
                }
                else
                {
                    // For a continuation, read ahead here to avoid having to build and then tear
                    // down the call stack if there is more than one buffer fetch necessary.
                    if (!JsonConverter.SingleValueReadWithReadAhead(ClassType.Value, ref reader, ref state))
                    {
                        state.BytesConsumed += reader.BytesConsumed;
                        return;
                    }
                }

                bool success = converter.TryReadAsObject(ref reader, jsonPropertyInfo.RuntimePropertyType !, options, ref state, out object?value);
                if (success)
                {
                    state.Current.ReturnValue = value;

                    // Read any trailing whitespace.
                    // If additional whitespace exists after this read, the subsequent call to reader.Read() will throw.
                    reader.Read();
                }

                state.BytesConsumed += reader.BytesConsumed;
            }
            catch (JsonReaderException ex)
            {
                // Re-throw with Path information.
                ThrowHelper.ReThrowWithPath(state, ex);
            }
            catch (FormatException ex) when(ex.Source == ThrowHelper.ExceptionSourceValueToRethrowAsJsonException)
            {
                ThrowHelper.ReThrowWithPath(state, reader, ex);
            }
            catch (InvalidOperationException ex) when(ex.Source == ThrowHelper.ExceptionSourceValueToRethrowAsJsonException)
            {
                ThrowHelper.ReThrowWithPath(state, reader, ex);
            }
            catch (JsonException ex)
            {
                ThrowHelper.AddExceptionInformation(state, reader, ex);
                throw;
            }
        }
        private static void AddNullToCollection(JsonPropertyInfo jsonPropertyInfo, ref Utf8JsonReader reader, ref ReadStack state)
        {
            JsonPropertyInfo?elementPropertyInfo = jsonPropertyInfo.ElementClassInfo !.PolicyProperty;

            // if elementPropertyInfo == null then this element doesn't need a converter (an object).
            if (elementPropertyInfo?.CanBeNull == false)
            {
                // Allow a value type converter to return a null value representation.
                // Most likely this will throw JsonException unless the converter has special logic (like converter for JsonElement).
                elementPropertyInfo.ReadEnumerable(JsonTokenType.Null, ref state, ref reader);
            }
            else
            {
                // Assume collection types are reference types and can have null assigned.
                ApplyObjectToEnumerable(null, ref state);
            }
        }
Esempio n. 19
0
        // If this method is changed, also change ApplyValueToEnumerable.
        internal static void ApplyObjectToEnumerable(
            object value,
            ref ReadStack state,
            ref Utf8JsonReader reader,
            bool setPropertyDirectly = false)
        {
            Debug.Assert(!state.Current.SkipProperty);

            if (state.Current.IsEnumerable)
            {
                if (state.Current.TempEnumerableValues != null)
                {
                    state.Current.TempEnumerableValues.Add(value);
                }
                else
                {
                    if (!(state.Current.ReturnValue is IList list))
                    {
                        ThrowHelper.ThrowJsonException_DeserializeUnableToConvertValue(value.GetType(), reader, state.JsonPath);
                        return;
                    }
                    list.Add(value);
                }
            }
            else if (!setPropertyDirectly && state.Current.IsEnumerableProperty)
            {
                Debug.Assert(state.Current.JsonPropertyInfo != null);
                Debug.Assert(state.Current.ReturnValue != null);
                if (state.Current.TempEnumerableValues != null)
                {
                    state.Current.TempEnumerableValues.Add(value);
                }
                else
                {
                    IList list = (IList)state.Current.JsonPropertyInfo.GetValueAsObject(state.Current.ReturnValue);
                    if (list == null)
                    {
                        state.Current.JsonPropertyInfo.SetValueAsObject(state.Current.ReturnValue, value);
                    }
                    else
                    {
                        list.Add(value);
                    }
                }
            }
            else if (state.Current.IsDictionary || (state.Current.IsDictionaryProperty && !setPropertyDirectly))
            {
                Debug.Assert(state.Current.ReturnValue != null);
                IDictionary dictionary = (IDictionary)state.Current.JsonPropertyInfo.GetValueAsObject(state.Current.ReturnValue);

                string key = state.Current.KeyName;
                Debug.Assert(!string.IsNullOrEmpty(key));
                dictionary[key] = value;
            }
            else if (state.Current.IsIDictionaryConstructible ||
                     (state.Current.IsIDictionaryConstructibleProperty && !setPropertyDirectly))
            {
                Debug.Assert(state.Current.TempDictionaryValues != null);
                IDictionary dictionary = (IDictionary)state.Current.JsonPropertyInfo.GetValueAsObject(state.Current.TempDictionaryValues);

                string key = state.Current.KeyName;
                Debug.Assert(!string.IsNullOrEmpty(key));
                dictionary[key] = value;
            }
            else
            {
                Debug.Assert(state.Current.JsonPropertyInfo != null);
                state.Current.JsonPropertyInfo.SetValueAsObject(state.Current.ReturnValue, value);
            }
        }
Esempio n. 20
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(
            ref Utf8JsonReader reader,
            ref ReadStack state,
            JsonSerializerOptions options)
        {
            JsonConverter converter = state.Current.JsonClassInfo.PropertyInfoForClassInfo.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 key = reader.GetString() !;

                // todo: https://github.com/dotnet/runtime/issues/32354
                state.Current.ReturnValue = state.ReferenceResolver.ResolveReference(key);
                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);
        }
Esempio n. 21
0
        private static bool HandleEndArray(
            JsonSerializerOptions options,
            ref Utf8JsonReader reader,
            ref ReadStack state)
        {
            bool lastFrame = state.IsLastFrame;

            if (state.Current.Drain)
            {
                // The array is not being applied to the object.
                state.Pop();
                return(lastFrame);
            }

            IEnumerable value = ReadStackFrame.GetEnumerableValue(state.Current);

            if (state.Current.TempEnumerableValues != null)
            {
                // We have a converter; possibilities:
                // - Add value to current frame's current property or TempEnumerableValues.
                // - Add value to previous frame's current property or TempEnumerableValues.
                // - Set current property on current frame to value.
                // - Set current property on previous frame to value.
                // - Set ReturnValue if root frame and value is the actual return value.
                JsonEnumerableConverter converter = state.Current.JsonPropertyInfo.EnumerableConverter;
                Debug.Assert(converter != null);

                value = converter.CreateFromList(ref state, (IList)value, options);
                state.Current.TempEnumerableValues = null;
            }
            else if (state.Current.IsEnumerableProperty)
            {
                // We added the items to the list already.
                state.Current.EndProperty();
                return(false);
            }

            if (lastFrame)
            {
                if (state.Current.ReturnValue == null)
                {
                    // Returning a converted list or object.
                    state.Current.Reset();
                    state.Current.ReturnValue = value;
                    return(true);
                }
                else if (state.Current.IsEnumerable || state.Current.IsDictionary || state.Current.IsIDictionaryConstructible)
                {
                    // Returning a non-converted list.
                    return(true);
                }
                // else there must be an outer object, so we'll return false here.
            }
            else if (state.Current.IsEnumerable)
            {
                state.Pop();
            }

            ApplyObjectToEnumerable(value, ref state, ref reader);

            return(false);
        }
Esempio n. 22
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(
            ref Utf8JsonReader reader,
            ref ReadStack state,
            JsonSerializerOptions options)
        {
            JsonConverter converter = state.Current.JsonClassInfo.PropertyInfoForClassInfo.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() !;

                // todo: https://github.com/dotnet/runtime/issues/32354
                state.Current.ReturnValue = state.ReferenceResolver.ResolveReference(referenceId);

                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);
        }
Esempio n. 23
0
        private void VerifyRead(JsonTokenType originalTokenType, int originalDepth, long bytesConsumed, ref ReadStack state, ref Utf8JsonReader reader)
        {
            switch (originalTokenType)
            {
            case JsonTokenType.StartArray:
                if (reader.TokenType != JsonTokenType.EndArray)
                {
                    // todo issue #38550 blocking this: originalDepth != reader.CurrentDepth + 1
                    ThrowHelper.ThrowJsonException_SerializationConverterRead(reader, state.JsonPath, ConverterBase.ToString());
                }
                break;

            case JsonTokenType.StartObject:
                if (reader.TokenType != JsonTokenType.EndObject)
                {
                    // todo issue #38550 blocking this: originalDepth != reader.CurrentDepth + 1
                    ThrowHelper.ThrowJsonException_SerializationConverterRead(reader, state.JsonPath, ConverterBase.ToString());
                }
                break;

            default:
                // Reading a single property value.
                if (reader.TokenType != originalTokenType || reader.BytesConsumed != bytesConsumed)
                {
                    ThrowHelper.ThrowJsonException_SerializationConverterRead(reader, state.JsonPath, ConverterBase.ToString());
                }

                break;
            }
        }
Esempio n. 24
0
 private static bool TryReadAheadMetadataAndSetState(ref Utf8JsonReader reader, ref ReadStack state, StackFrameObjectState nextState)
 {
     // If we can't read here, the read will be completed at the root API by asking the stream for more data.
     // Set the state so we know where to resume on re-entry.
     state.Current.ObjectState = nextState;
     return(reader.Read());
 }
Esempio n. 25
0
 public static void ReThrowWithPath(ref ReadStack state, in Utf8JsonReader reader, Exception ex)
Esempio n. 26
0
        private static void HandleValue(JsonTokenType tokenType, JsonSerializerOptions options, ref Utf8JsonReader reader, ref ReadStack state)
        {
            if (state.Current.SkipProperty)
            {
                return;
            }

            if (state.Current.LastSeenMetadataProperty == MetadataPropertyName.Id || state.Current.LastSeenMetadataProperty == MetadataPropertyName.Ref)
            {
                Debug.Assert(options.ReferenceHandling.ShouldReadPreservedReferences());

                HandleMetadataPropertyValue(ref reader, ref state);
                return;
            }

            JsonPropertyInfo?jsonPropertyInfo = state.Current.JsonPropertyInfo;

            Debug.Assert(state.Current.JsonClassInfo != null);
            if (jsonPropertyInfo == null)
            {
                jsonPropertyInfo = state.Current.JsonClassInfo.CreateRootProperty(options);
            }
            else if (state.Current.JsonClassInfo.ClassType == ClassType.Unknown)
            {
                jsonPropertyInfo = state.Current.JsonClassInfo.GetOrAddPolymorphicProperty(jsonPropertyInfo, typeof(object), options);
            }

            jsonPropertyInfo.Read(tokenType, ref state, ref reader);
        }
Esempio n. 27
0
        private static void HandleEndDictionary(JsonSerializerOptions options, ref Utf8JsonReader reader, ref ReadStack state)
        {
            if (state.Current.SkipProperty)
            {
                // Todo: determine if this is reachable.
                return;
            }

            if (state.Current.IsDictionaryProperty)
            {
                // Handle special case of DataExtensionProperty where we just added a dictionary element to the extension property.
                // Since the JSON value is not a dictionary element (it's a normal property in JSON) a JsonTokenType.EndObject
                // encountered here is from the outer object so forward to HandleEndObject().
                if (state.Current.JsonClassInfo.DataExtensionProperty == state.Current.JsonPropertyInfo)
                {
                    HandleEndObject(ref reader, ref state);
                }
                else
                {
                    // We added the items to the dictionary already.
                    state.Current.EndProperty();
                }
            }
            else if (state.Current.IsIDictionaryConstructibleProperty)
            {
                Debug.Assert(state.Current.TempDictionaryValues != null);
                JsonDictionaryConverter converter = state.Current.JsonPropertyInfo.DictionaryConverter;
                state.Current.JsonPropertyInfo.SetValueAsObject(state.Current.ReturnValue, converter.CreateFromDictionary(ref state, state.Current.TempDictionaryValues, options));
                state.Current.EndProperty();
            }
            else
            {
                object value;
                if (state.Current.TempDictionaryValues != null)
                {
                    JsonDictionaryConverter converter = state.Current.JsonPropertyInfo.DictionaryConverter;
                    value = converter.CreateFromDictionary(ref state, state.Current.TempDictionaryValues, options);
                }
                else
                {
                    value = state.Current.ReturnValue;
                }

                if (state.IsLastFrame)
                {
                    // Set the return value directly since this will be returned to the user.
                    state.Current.Reset();
                    state.Current.ReturnValue = value;
                }
                else
                {
                    state.Pop();
                    ApplyObjectToEnumerable(value, ref state, ref reader);
                }
            }
        }
        private static TValue ReadCore <TValue>(JsonConverter jsonConverter, ref Utf8JsonReader reader, JsonSerializerOptions options, ref ReadStack state)
        {
            if (jsonConverter is JsonConverter <TValue> converter)
            {
                // Call the strongly-typed ReadCore that will not box structs.
                return(converter.ReadCore(ref reader, options, ref state));
            }

            // The non-generic API was called or we have a polymorphic case where TValue is not equal to the T in JsonConverter<T>.
            object?value = jsonConverter.ReadCoreAsObject(ref reader, options, ref state);

            Debug.Assert(value == null || value is TValue);
            return((TValue)value !);
        }
Esempio n. 29
0
        internal static JsonPropertyInfo LookupProperty(
            object obj,
            ref Utf8JsonReader reader,
            JsonSerializerOptions options,
            ref ReadStack state,
            out bool useExtensionProperty)
        {
            Debug.Assert(state.Current.JsonClassInfo.ClassType == ClassType.Object);

            JsonPropertyInfo jsonPropertyInfo;

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

            if (reader._stringHasEscaping)
            {
                int idx = propertyName.IndexOf(JsonConstants.BackSlash);
                Debug.Assert(idx != -1);
                unescapedPropertyName = GetUnescapedString(propertyName, idx);
            }
            else
            {
                unescapedPropertyName = propertyName;
            }

            if (options.ReferenceHandling.ShouldReadPreservedReferences())
            {
                if (propertyName.Length > 0 && propertyName[0] == '$')
                {
                    ThrowHelper.ThrowUnexpectedMetadataException(propertyName, ref reader, ref state);
                }
            }

            jsonPropertyInfo = state.Current.JsonClassInfo.GetProperty(unescapedPropertyName, ref state.Current);

            // Increment PropertyIndex so GetProperty() starts with the next property the next time this function is called.
            state.Current.PropertyIndex++;

            // Determine if we should use the extension property.
            if (jsonPropertyInfo == JsonPropertyInfo.s_missingProperty)
            {
                JsonPropertyInfo?dataExtProperty = state.Current.JsonClassInfo.DataExtensionProperty;
                if (dataExtProperty != null)
                {
                    state.Current.JsonPropertyNameAsString = JsonHelpers.Utf8GetString(unescapedPropertyName);
                    CreateDataExtensionProperty(obj, dataExtProperty);
                    jsonPropertyInfo = dataExtProperty;
                }

                state.Current.JsonPropertyInfo = jsonPropertyInfo;
                useExtensionProperty           = true;
                return(jsonPropertyInfo);
            }

            // Support JsonException.Path.
            Debug.Assert(
                jsonPropertyInfo.JsonPropertyName == null ||
                options.PropertyNameCaseInsensitive ||
                unescapedPropertyName.SequenceEqual(jsonPropertyInfo.JsonPropertyName));

            state.Current.JsonPropertyInfo = jsonPropertyInfo;

            if (jsonPropertyInfo.JsonPropertyName == null)
            {
                byte[] propertyNameArray = unescapedPropertyName.ToArray();
                if (options.PropertyNameCaseInsensitive)
                {
                    // Each payload can have a different name here; remember the value on the temporary stack.
                    state.Current.JsonPropertyName = propertyNameArray;
                }
                else
                {
                    // Prevent future allocs by caching globally on the JsonPropertyInfo which is specific to a Type+PropertyName
                    // so it will match the incoming payload except when case insensitivity is enabled (which is handled above).
                    state.Current.JsonPropertyInfo.JsonPropertyName = propertyNameArray;
                }
            }

            state.Current.JsonPropertyInfo = jsonPropertyInfo;
            useExtensionProperty           = false;
            return(jsonPropertyInfo);
        }
Esempio n. 30
0
        private static void HandleStartArray(
            JsonSerializerOptions options,
            ref Utf8JsonReader reader,
            ref ReadStack state)
        {
            if (state.Current.SkipProperty)
            {
                // The array is not being applied to the object.
                state.Push();
                state.Current.Drain = true;
                return;
            }

            JsonPropertyInfo jsonPropertyInfo = state.Current.JsonPropertyInfo;

            if (jsonPropertyInfo == null)
            {
                jsonPropertyInfo = state.Current.JsonClassInfo.CreateRootObject(options);
            }

            // Verify that we are processing a valid enumerable or dictionary.
            if (((ClassType.Enumerable | ClassType.Dictionary | ClassType.IDictionaryConstructible) & jsonPropertyInfo.ClassType) == 0)
            {
                ThrowHelper.ThrowJsonException_DeserializeUnableToConvertValue(jsonPropertyInfo.RuntimePropertyType);
            }

            if (state.Current.CollectionPropertyInitialized)
            {
                // An array nested in a dictionary or array, so push a new stack frame.
                Type elementType = jsonPropertyInfo.ElementClassInfo.Type;

                state.Push();
                state.Current.Initialize(elementType, options);
            }

            state.Current.CollectionPropertyInitialized = true;

            // The current JsonPropertyInfo will be null if the current type is not one of
            // ClassType.Value | ClassType.Enumerable | ClassType.Dictionary.
            // We should not see ClassType.Value here because we handle it on a different code
            // path invoked in the main read loop.
            // Only ClassType.Enumerable is valid here since we just saw a StartArray token.
            if (state.Current.JsonPropertyInfo == null ||
                state.Current.JsonPropertyInfo.ClassType != ClassType.Enumerable)
            {
                ThrowHelper.ThrowJsonException_DeserializeUnableToConvertValue(state.Current.JsonClassInfo.Type);
            }

            // Set or replace the existing enumerable value.
            object value = ReadStackFrame.CreateEnumerableValue(ref reader, ref state);

            // If value is not null, then we don't have a converter so apply the value.
            if (value != null)
            {
                if (state.Current.ReturnValue != null)
                {
                    state.Current.JsonPropertyInfo.SetValueAsObject(state.Current.ReturnValue, value);
                }
                else
                {
                    // Primitive arrays being returned without object
                    state.Current.SetReturnValue(value);
                }
            }
        }