Example #1
0
 protected internal override void ReadEnumerable(JsonSerializerOptions options, ref ReadObjectState current, ref Utf8JsonReader reader)
 {
     if (ValueConverter != null)
     {
         Type propertyType = PropertyType;
         if (propertyType.IsGenericType && propertyType.GetGenericTypeDefinition() == typeof(Nullable <>))
         {
             propertyType = Nullable.GetUnderlyingType(propertyType);
         }
         object value = ValueConverter.GetRead(ref reader, propertyType);
         ReadObjectState.SetReturnValue(value, options, ref current);
     }
     else
     {
         if (this is IJsonSerializerInternal <TValue> converter)
         {
             TValue value = converter.Read(ref reader);
             ReadObjectState.SetReturnValue(value, options, ref current);
         }
         else
         {
             throw new InvalidOperationException($"todo: there is no converter for {PropertyType}");
         }
     }
 }
Example #2
0
        private static void HandleStartObject(JsonSerializerOptions options, ref ReadObjectState current, ref List <ReadObjectState> previous, ref int arrayIndex)
        {
            Type objType;

            if (current.IsEnumerable() || current.IsPropertyEnumerable())
            {
                // An array of objects either on the current property or on a list
                objType = current.GetElementType();
                JsonPropertyInfo propInfo = current.PropertyInfo;
                SetPreviousState(ref previous, current, arrayIndex++);
                current.Reset();

                current.ClassInfo   = options.GetOrAddClass(objType);
                current.ReturnValue = current.ClassInfo.CreateObject();
            }
            else if (current.PropertyInfo != null)
            {
                // Nested object
                objType = current.PropertyInfo.PropertyType;
                SetPreviousState(ref previous, current, arrayIndex++);
                current.Reset();

                current.ClassInfo   = options.GetOrAddClass(objType);
                current.ReturnValue = current.ClassInfo.CreateObject();
            }
            else
            {
                current.ReturnValue = current.ClassInfo.CreateObject();
            }
        }
Example #3
0
        protected internal override void ReadEnumerable(JsonTokenType tokenType, JsonSerializerOptions options, ref ReadObjectState current, ref Utf8JsonReader reader)
        {
            if (ValueConverter != null)
            {
                Type propertyType = PropertyType;
                if (propertyType.IsGenericType && propertyType.GetGenericTypeDefinition() == typeof(Nullable <>))
                {
                    propertyType = Nullable.GetUnderlyingType(propertyType);
                }

                if (!ValueConverter.TryRead(propertyType, ref reader, out TValue value))
                {
                    throw new JsonReaderException("todo: unable to read value (propertypath)", 0, 0);
                }

                ReadObjectState.SetReturnValue(value, options, ref current);
            }
            else
            {
                if (this is IJsonValueConverter <TValue> converter)
                {
                    if (!converter.TryRead(PropertyType, ref reader, out TValue value))
                    {
                        throw new JsonReaderException("todo: unable to read value (propertypath)", 0, 0);
                    }

                    ReadObjectState.SetReturnValue(value, options, ref current);
                }
                else
                {
                    throw new InvalidOperationException($"todo: there is no converter for {PropertyType}");
                }
            }
        }
Example #4
0
        private static bool HandleNull(ref ReadObjectState current, JsonSerializerOptions options)
        {
            Debug.Assert(current.PropertyInfo != null);

            JsonPropertyInfo propertyInfo = current.PropertyInfo;

            if (!propertyInfo.CanBeNull)
            {
                throw new InvalidOperationException($"todo: {propertyInfo.PropertyType} can't be null");
            }

            if (current.IsEnumerable() || current.IsPropertyEnumerable())
            {
                ReadObjectState.SetReturnValue(null, options, ref current);
                return(false);
            }

            if (current.ReturnValue == null)
            {
                return(true);
            }

            if (!propertyInfo.IgnoreNullPropertyValueOnRead(options))
            {
                current.PropertyInfo.SetValueAsObject(current.ReturnValue, null, options);
            }

            return(false);
        }
Example #5
0
        private static object Read(
            Utf8JsonReader reader,
            Type returnType,
            JsonSerializerOptions options)
        {
            if (options == null)
            {
                options = s_defaultSettings;
            }

            List <ReadObjectState> previous = null;
            int arrayIndex = 0;

            ReadObjectState current   = default;
            JsonClassInfo   classInfo = options.GetOrAddClass(returnType);

            current.ClassInfo = classInfo;
            if (classInfo.ClassType != ClassType.Object)
            {
                current.PropertyInfo = classInfo.GetPolicyProperty();
            }

            Read(returnType, options, ref reader, ref current, ref previous, ref arrayIndex);

            return(current.ReturnValue);
        }
        private static void HandleStartArray(
            JsonSerializerOptions options,
            ref Utf8JsonReader reader,
            ref ReadObjectState current,
            ref List <ReadObjectState> previous,
            ref int arrayIndex)
        {
            Type arrayType = current.PropertyInfo.PropertyType;

            if (!typeof(IEnumerable).IsAssignableFrom(arrayType) || (arrayType.IsArray && arrayType.GetArrayRank() > 1))
            {
                throw new JsonReaderException($"todo: type {arrayType.ToString()} is not convertable to array.", 0, 0);
            }

            Debug.Assert(current.IsPropertyEnumerable());
            if (current.IsPropertyEnumerable())
            {
                if (current.EnumerableCreated)
                {
                    // A nested json array so push a new stack frame.
                    Type elementType = current.ClassInfo.ElementClassInfo.GetPolicyProperty().PropertyType;

                    SetPreviousState(ref previous, current, arrayIndex++);
                    current.Reset();
                    current.ClassInfo          = options.GetOrAddClass(elementType);
                    current.PropertyInfo       = current.ClassInfo.GetPolicyProperty();
                    current.PopStackOnEndArray = true;
                }
                else
                {
                    current.EnumerableCreated = true;
                }

                // If current property is already set (from a constructor, for example) leave as-is
                if (current.PropertyInfo.GetValueAsObject(current.ReturnValue, options) == null)
                {
                    // Create the enumerable.
                    object value = ReadObjectState.CreateEnumerableValue(ref current, options);
                    if (value != null)
                    {
                        if (current.ReturnValue != null)
                        {
                            current.PropertyInfo.SetValueAsObject(current.ReturnValue, value, options);
                        }
                        else
                        {
                            // Primitive arrays being returned without object
                            current.SetReturnValue(value, options);
                        }
                    }
                }
            }
        }
Example #7
0
        public override void Read(JsonSerializerOptions options, ref ReadObjectState current, ref Utf8JsonReader reader)
        {
            if (ElementClassInfo != null)
            {
                // Forward the setter to the value-based JsonPropertyInfo.
                JsonPropertyInfo propertyInfo = ElementClassInfo.GetPolicyProperty();
                propertyInfo.ReadEnumerable(options, ref current, ref reader);
            }
            else
            {
                if (ValueConverter != null)
                {
                    Type propertyType = PropertyType;
                    if (propertyType.IsGenericType && propertyType.GetGenericTypeDefinition() == typeof(Nullable <>))
                    {
                        propertyType = Nullable.GetUnderlyingType(propertyType);
                    }

                    object value = ValueConverter.GetRead(ref reader, propertyType);
                    if (value != null || !SkipNullValuesOnRead(options))
                    {
                        SetValueAsObject(current.ReturnValue, value, options);
                    }
                }
                else
                {
                    if (this is IJsonSerializerInternal <TValue> converter)
                    {
                        TValue value = converter.Read(ref reader);
                        if (current.ReturnValue == null)
                        {
                            current.ReturnValue = value;
                        }
                        else
                        {
                            if (value != null || !SkipNullValuesOnRead(options))
                            {
                                Set(current.ReturnValue, value);
                            }
                        }
                    }
                    else
                    {
                        throw new InvalidOperationException($"todo: there is no converter for {PropertyType}");
                    }
                }
            }
        }
Example #8
0
        private static async Task <T> ReadAsync <T>(PipeReader utf8Reader, Type returnType, JsonSerializerOptions options = null, CancellationToken cancellationToken = default)
        {
            if (options == null)
            {
                options = s_defaultSettings;
            }

            ReadObjectState current   = default;
            JsonClassInfo   classInfo = options.GetOrAddClass(returnType);

            current.ClassInfo = classInfo;
            if (classInfo.ClassType != ClassType.Object)
            {
                current.PropertyInfo = classInfo.GetPolicyProperty();
            }

            var readerState = new JsonReaderState(options: options.ReaderOptions);
            List <ReadObjectState> previous = null;
            int arrayIndex = 0;

            ReadResult result;

            do
            {
                result = await utf8Reader.ReadAsync(cancellationToken).ConfigureAwait(false);

                ReadOnlySequence <byte> buffer = result.Buffer;

                Read(
                    ref readerState,
                    returnType,
                    result.IsCompleted,
                    buffer,
                    options,
                    ref current,
                    ref previous,
                    ref arrayIndex);

                utf8Reader.AdvanceTo(buffer.GetPosition(readerState.BytesConsumed), buffer.End);
            } while (!result.IsCompleted);

            return((T)current.ReturnValue);
        }
Example #9
0
        private static bool HandleEndObject(JsonSerializerOptions options, ref ReadObjectState current, ref List <ReadObjectState> previous, ref int arrayIndex)
        {
            object value = current.ReturnValue;

            if (arrayIndex > 0)
            {
                ReadObjectState previousFrame = default;
                GetPreviousState(ref previous, ref previousFrame, --arrayIndex);
                current = previousFrame;
            }
            else
            {
                current.Reset();
                current.ReturnValue = value;
                return(true);
            }

            ReadObjectState.SetReturnValue(value, options, ref current);
            return(false);
        }
        private static void ReadCore(
            ref JsonReaderState readerState,
            Type returnType,
            bool isFinalBlock,
            ReadOnlySequence <byte> buffer,
            JsonSerializerOptions options,
            ref ReadObjectState current,
            ref List <ReadObjectState> previous,
            ref int arrayIndex)
        {
            Utf8JsonReader reader = new Utf8JsonReader(buffer, isFinalBlock, readerState);

            ReadCore(
                options,
                ref reader,
                ref current,
                ref previous,
                ref arrayIndex);

            readerState = reader.CurrentState;
        }
Example #11
0
        private static void ReadCore(
            ref JsonReaderState readerState,
            bool isFinalBlock,
            byte[] buffer,
            int bytesToRead,
            JsonSerializerOptions options,
            ref ReadObjectState current,
            ref List <ReadObjectState> previous,
            ref int arrayIndex)
        {
            Utf8JsonReader reader = new Utf8JsonReader(new ReadOnlySpan <byte>(buffer, 0, bytesToRead), isFinalBlock, readerState);

            ReadCore(
                options,
                ref reader,
                ref current,
                ref previous,
                ref arrayIndex);

            readerState = reader.CurrentState;
        }
Example #12
0
        public static object CreateEnumerableValue(ref ReadObjectState current, JsonSerializerOptions options)
        {
            // If the property has an EnumerableConverter, then we use tempEnumerableValues.
            if (current.PropertyInfo.EnumerableConverter != null)
            {
                current.TempEnumerableValues = new List <object>();
                return(null);
            }

            Type propType = current.PropertyInfo.PropertyType;

            if (typeof(IList).IsAssignableFrom(propType))
            {
                // If IList, add the members as we create them.
                JsonClassInfo collectionClassInfo = options.GetOrAddClass(propType);
                IList         collection          = (IList)collectionClassInfo.CreateObject();
                return(collection);
            }
            else
            {
                throw new InvalidOperationException($"todo: IEnumerable type {propType.ToString()} is not convertable.");
            }
        }
        private static bool HandleValue(JsonTokenType tokenType, JsonSerializerOptions options, ref Utf8JsonReader reader, ref ReadObjectState current)
        {
            if (current.PropertyInfo == null)
            {
                return(false);
            }

            bool lastCall = (!current.IsEnumerable() && !current.IsPropertyEnumerable() && current.ReturnValue == null);

            current.PropertyInfo.Read(tokenType, options, ref current, ref reader);
            return(lastCall);
        }
Example #14
0
        private static async ValueTask <TValue> ReadAsync <TValue>(Stream utf8Json, Type returnType, JsonSerializerOptions options = null, CancellationToken cancellationToken = default)
        {
            if (options == null)
            {
                options = s_defaultSettings;
            }

            ReadObjectState current   = default;
            JsonClassInfo   classInfo = options.GetOrAddClass(returnType);

            current.ClassInfo = classInfo;
            if (classInfo.ClassType != ClassType.Object)
            {
                current.PropertyInfo = classInfo.GetPolicyProperty();
            }

            var readerState = new JsonReaderState(options: options.ReaderOptions);
            List <ReadObjectState> previous = null;
            int arrayIndex = 0;

            int bytesRemaining = 0;
            int bytesRead;

            // todo: switch to ArrayBuffer implementation to handle the allocs?
            byte[] buffer = ArrayPool <byte> .Shared.Rent(options.EffectiveBufferSize);

            int  bufferSize = buffer.Length;
            bool isFinalBlock;

            try
            {
                do
                {
                    int bytesToRead = bufferSize - bytesRemaining;
                    bytesRead = await utf8Json.ReadAsync(buffer, bytesRemaining, bytesToRead, cancellationToken).ConfigureAwait(false);

                    int deserializeBufferSize = bytesRemaining + bytesRead;
                    isFinalBlock = (bytesRead == 0);

                    ReadCore(
                        ref readerState,
                        isFinalBlock,
                        buffer,
                        deserializeBufferSize,
                        options,
                        ref current,
                        ref previous,
                        ref arrayIndex);

                    if (isFinalBlock)
                    {
                        return((TValue)current.ReturnValue);
                    }

                    // We have to shift or expand the buffer because there wasn't enough data to complete deserialization.
                    int bytesConsumed = (int)readerState.BytesConsumed;
                    bytesRemaining = deserializeBufferSize - bytesConsumed;

                    if (bytesConsumed <= (bufferSize / 2))
                    {
                        // We have less than half the buffer available, double the buffer size.
                        bufferSize = (bufferSize < HalfMaxValue) ? bufferSize * 2 : int.MaxValue;

                        byte[] dest = ArrayPool <byte> .Shared.Rent(bufferSize);

                        bufferSize = dest.Length;
                        if (bytesRemaining > 0)
                        {
                            // Copy the unprocessed data to the new buffer while shifting the processed bytes.
                            Buffer.BlockCopy(buffer, bytesConsumed, dest, 0, bytesRemaining);
                        }
                        ArrayPool <byte> .Shared.Return(buffer, clearArray : true);

                        buffer = dest;
                    }
                    else if (bytesRemaining > 0)
                    {
                        // Shift the processed bytes to the beginning of buffer to make more room.
                        Buffer.BlockCopy(buffer, bytesConsumed, buffer, 0, bytesRemaining);
                    }
                } while (!isFinalBlock);
            }
            finally
            {
                ArrayPool <byte> .Shared.Return(buffer, clearArray : true);
            }

            throw new InvalidOperationException("todo");
        }
Example #15
0
        private static bool HandleValue(JsonSerializerOptions options, ref Utf8JsonReader reader, ref ReadObjectState current)
        {
            Debug.Assert(current.PropertyInfo != null);

            bool lastCall = (!current.IsEnumerable() && !current.IsPropertyEnumerable() && current.ReturnValue == null);

            current.PropertyInfo.Read(options, ref current, ref reader);

            return(lastCall);
        }
Example #16
0
 protected internal abstract void ReadEnumerable(JsonSerializerOptions options, ref ReadObjectState current, ref Utf8JsonReader reader);
Example #17
0
 public abstract void Read(JsonSerializerOptions options, ref ReadObjectState current, ref Utf8JsonReader reader);
        private static bool HandleEndArray(
            JsonSerializerOptions options,
            ref ReadObjectState current,
            ref List <ReadObjectState> previous,
            ref int arrayIndex)
        {
            IEnumerable value = ReadObjectState.GetEnumerableValue(current);

            if (value == null)
            {
                // We added the items to the list property already.
                current.ResetProperty();
                return(false);
            }

            bool lastFrame = (arrayIndex == 0);

            bool setPropertyDirectly;

            if (current.TempEnumerableValues != null)
            {
                JsonEnumerableConverter converter = current.PropertyInfo.EnumerableConverter;
                if (converter == null)
                {
                    converter = current.ClassInfo.EnumerableConverter;
                }

                Type elementType = current.GetElementType();
                value = converter.CreateFromList(elementType, (IList)value);
                setPropertyDirectly = true;
            }
            else
            {
                setPropertyDirectly = false;
            }

            if (current.PopStackOnEndArray)
            {
                ReadObjectState previousFrame = default;
                GetPreviousState(ref previous, ref previousFrame, --arrayIndex);
                current = previousFrame;
            }

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

            ReadObjectState.SetReturnValue(value, options, ref current, setPropertyDirectly: setPropertyDirectly);
            return(false);
        }
Example #19
0
        public override void Read(JsonTokenType tokenType, JsonSerializerOptions options, ref ReadObjectState current, ref Utf8JsonReader reader)
        {
            if (ElementClassInfo != null)
            {
                // Forward the setter to the value-based JsonPropertyInfo.
                JsonPropertyInfo propertyInfo = ElementClassInfo.GetPolicyProperty();
                propertyInfo.ReadEnumerable(tokenType, options, ref current, ref reader);
            }
            else if (HasSetter)
            {
                if (ValueConverter != null)
                {
                    Type propertyType = PropertyType;
                    if (propertyType.IsGenericType && propertyType.GetGenericTypeDefinition() == typeof(Nullable <>))
                    {
                        propertyType = Nullable.GetUnderlyingType(propertyType);
                    }

                    if (!ValueConverter.TryRead(propertyType, ref reader, out TValue value))
                    {
                        throw new JsonReaderException("todo: unable to read value (propertypath)", 0, 0);
                    }

                    if (value != null || !IgnoreNullPropertyValueOnRead(options))
                    {
                        SetValueAsObject(current.ReturnValue, value, options);
                    }
                }
                else
                {
                    if (this is IJsonValueConverter <TValue> converter)
                    {
                        if (!converter.TryRead(PropertyType, ref reader, out TValue value))
                        {
                            throw new JsonReaderException("todo: unable to read value (propertypath)", 0, 0);
                        }

                        if (current.ReturnValue == null)
                        {
                            current.ReturnValue = value;
                        }
                        else
                        {
                            if (value != null || !IgnoreNullPropertyValueOnRead(options))
                            {
                                Set(current.ReturnValue, value);
                            }
                        }
                    }
                    else
                    {
                        throw new InvalidOperationException($"todo: there is no converter for {PropertyType}");
                    }
                }
            }
        }
        // todo: refactor this method to split by ClassType(Enumerable, Object, or Value) like Write()
        private static void ReadCore(
            JsonSerializerOptions options,
            ref Utf8JsonReader reader,
            ref ReadObjectState current,
            ref List <ReadObjectState> previous,
            ref int arrayIndex)
        {
            while (reader.Read())
            {
                JsonTokenType tokenType = reader.TokenType;

                if (tokenType >= JsonTokenType.String && tokenType <= JsonTokenType.False)
                {
                    Debug.Assert(tokenType == JsonTokenType.String || tokenType == JsonTokenType.Number || tokenType == JsonTokenType.True || tokenType == JsonTokenType.False);

                    if (HandleValue(tokenType, options, ref reader, ref current))
                    {
                        // todo: verify bytes read == bytes processed.
                        return;
                    }
                }
                else if (tokenType == JsonTokenType.PropertyName)
                {
                    Debug.Assert(current.ReturnValue != default);
                    Debug.Assert(current.ClassInfo != default);

                    ReadOnlySpan <byte> propertyName = reader.HasValueSequence ? reader.ValueSequence.ToArray() : reader.ValueSpan;
                    current.PropertyInfo = current.ClassInfo.GetProperty(propertyName, current.PropertyIndex);
                    current.PropertyIndex++;
                }
                else if (tokenType == JsonTokenType.StartObject)
                {
                    HandleStartObject(options, ref current, ref previous, ref arrayIndex);
                }
                else if (tokenType == JsonTokenType.EndObject)
                {
                    if (HandleEndObject(options, ref current, ref previous, ref arrayIndex))
                    {
                        // todo: verify bytes read == bytes processed.
                        return;
                    }
                }
                else if (tokenType == JsonTokenType.StartArray)
                {
                    HandleStartArray(options, ref reader, ref current, ref previous, ref arrayIndex);
                }
                else if (tokenType == JsonTokenType.EndArray)
                {
                    if (HandleEndArray(options, ref current, ref previous, ref arrayIndex))
                    {
                        // todo: verify bytes read == bytes processed.
                        return;
                    }
                }
                else if (tokenType == JsonTokenType.Null)
                {
                    if (HandleNull(ref current, options))
                    {
                        return;
                    }
                }
            }

            return;
        }