Beispiel #1
0
        internal virtual void GetPolicies(JsonSerializerOptions options)
        {
            if (RuntimePropertyType.IsArray)
            {
                EnumerableConverter = new DefaultArrayConverter();
            }
            else if (typeof(IEnumerable).IsAssignableFrom(RuntimePropertyType))
            {
                Type elementType = JsonClassInfo.GetElementType(RuntimePropertyType);

                if (RuntimePropertyType.IsAssignableFrom(typeof(JsonEnumerableT <>).MakeGenericType(elementType)))
                {
                    EnumerableConverter = new DefaultEnumerableConverter();
                }
            }
        }
Beispiel #2
0
        private static byte[] WriteInternal(object value, Type type, JsonSerializerOptions options)
        {
            if (options == null)
            {
                options = s_defaultSettings;
            }

            byte[] result;
            var    state = new JsonWriterState(options.WriterOptions);

            using (var output = new ArrayBufferWriter <byte>(options.EffectiveBufferSize))
            {
                var writer = new Utf8JsonWriter(output, state);

                if (value == null)
                {
                    writer.WriteNullValue();
                }
                else
                {
                    if (type == null)
                    {
                        type = value.GetType();
                    }

                    WriteObjectState current = default;

                    JsonClassInfo classInfo = options.GetOrAddClass(type);
                    current.ClassInfo    = classInfo;
                    current.CurrentValue = value;
                    if (classInfo.ClassType != ClassType.Object)
                    {
                        current.PropertyInfo = classInfo.GetPolicyProperty();
                    }

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

                    Write(ref writer, -1, options, ref current, ref previous, ref arrayIndex);
                }

                writer.Flush(isFinalBlock: true);
                result = output.WrittenMemory.ToArray();
            }

            return(result);
        }
Beispiel #3
0
        public void Push(JsonClassInfo nextClassInfo, object nextValue)
        {
            Push();
            Current.JsonClassInfo = nextClassInfo;
            Current.CurrentValue  = nextValue;

            if (nextClassInfo.ClassType == ClassType.Enumerable)
            {
                Current.PopStackOnEndArray = true;
                Current.JsonPropertyInfo   = Current.JsonClassInfo.GetPolicyProperty();
            }
            else
            {
                Debug.Assert(nextClassInfo.ClassType == ClassType.Object || nextClassInfo.ClassType == ClassType.Unknown);
                Current.PopStackOnEndObject = true;
            }
        }
Beispiel #4
0
        internal JsonPropertyInfo GetJsonPropertyInfoFromClassInfo(JsonClassInfo classInfo, JsonSerializerOptions options)
        {
            if (classInfo.ClassType != ClassType.Object)
            {
                return(classInfo.GetPolicyProperty());
            }

            Type objectType = classInfo.Type;

            if (!_objectJsonProperties.TryGetValue(objectType, out JsonPropertyInfo propertyInfo))
            {
                propertyInfo = JsonClassInfo.CreateProperty(objectType, objectType, null, typeof(object), options);
                _objectJsonProperties[objectType] = propertyInfo;
            }

            return(propertyInfo);
        }
Beispiel #5
0
 public virtual void Initialize(
     Type parentClassType,
     Type declaredPropertyType,
     Type runtimePropertyType,
     PropertyInfo propertyInfo,
     Type elementType,
     JsonSerializerOptions options)
 {
     ParentClassType      = parentClassType;
     DeclaredPropertyType = declaredPropertyType;
     RuntimePropertyType  = runtimePropertyType;
     PropertyInfo         = propertyInfo;
     ClassType            = JsonClassInfo.GetClassType(runtimePropertyType);
     _elementType         = elementType;
     Options        = options;
     IsNullableType = runtimePropertyType.IsGenericType && runtimePropertyType.GetGenericTypeDefinition() == typeof(Nullable <>);
     CanBeNull      = IsNullableType || !runtimePropertyType.IsValueType;
 }
Beispiel #6
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);
        }
        private static void HandleStartObject(JsonSerializerOptions options, ref Utf8JsonReader reader, ref ReadStack state)
        {
            Debug.Assert(!state.Current.IsProcessingDictionary && !state.Current.IsProcessingImmutableDictionary);

            if (state.Current.IsProcessingEnumerable)
            {
                // A nested object within an enumerable.
                Type objType = state.Current.GetElementType();
                state.Push();
                state.Current.Initialize(objType, options);
            }
            else if (state.Current.JsonPropertyInfo != null)
            {
                // Nested object.
                Type objType = state.Current.JsonPropertyInfo.RuntimePropertyType;
                state.Push();
                state.Current.Initialize(objType, options);
            }

            JsonClassInfo classInfo = state.Current.JsonClassInfo;

            if (classInfo.CreateObject is null && classInfo.ClassType == ClassType.Object)
            {
                if (classInfo.Type.IsInterface)
                {
                    ThrowHelper.ThrowInvalidOperationException_DeserializePolymorphicInterface(classInfo.Type);
                }
                else
                {
                    ThrowHelper.ThrowInvalidOperationException_DeserializeMissingParameterlessConstructor(classInfo.Type);
                }
            }

            if (state.Current.IsProcessingImmutableDictionary)
            {
                state.Current.TempDictionaryValues = (IDictionary)classInfo.CreateObject();
            }
            else
            {
                state.Current.ReturnValue = classInfo.CreateObject();
            }
        }
Beispiel #8
0
        private static bool WriteObject(
            JsonSerializerOptions options,
            Utf8JsonWriter writer,
            ref WriteStack state)
        {
            JsonClassInfo classInfo = state.Current.JsonClassInfo;

            // Write the start.
            if (!state.Current.StartObjectWritten)
            {
                if (state.Current.JsonPropertyInfo?._escapedName == null)
                {
                    writer.WriteStartObject();
                }
                else
                {
                    writer.WriteStartObject(state.Current.JsonPropertyInfo._escapedName);
                }
                state.Current.StartObjectWritten = true;
            }

            // Determine if we are done enumerating properties.
            if (state.Current.PropertyIndex != classInfo.PropertyCount)
            {
                HandleObject(options, writer, ref state);
                return(false);
            }

            writer.WriteEndObject();

            if (state.Current.PopStackOnEndObject)
            {
                state.Pop();
            }
            else
            {
                state.Current.EndObject();
            }

            return(true);
        }
Beispiel #9
0
        internal JsonPropertyInfo(
            Type parentClassType,
            Type declaredPropertyType,
            Type runtimePropertyType,
            PropertyInfo propertyInfo,
            Type elementType,
            JsonSerializerOptions options)
        {
            ParentClassType      = parentClassType;
            DeclaredPropertyType = declaredPropertyType;
            RuntimePropertyType  = runtimePropertyType;
            PropertyInfo         = propertyInfo;
            ClassType            = JsonClassInfo.GetClassType(runtimePropertyType);
            if (elementType != null)
            {
                ElementClassInfo = options.GetOrAddClass(elementType);
            }

            IsNullableType = runtimePropertyType.IsGenericType && runtimePropertyType.GetGenericTypeDefinition() == typeof(Nullable <>);
            CanBeNull      = IsNullableType || !runtimePropertyType.IsValueType;
        }
Beispiel #10
0
        // todo: to minimize hashtable lookups, cache JsonClassInfo:
        //public JsonClassInfo ClassInfo;

        public virtual void Initialize(
            Type parentClassType,
            Type declaredPropertyType,
            Type runtimePropertyType,
            PropertyInfo propertyInfo,
            Type elementType,
            JsonSerializerOptions options)
        {
            ParentClassType      = parentClassType;
            DeclaredPropertyType = declaredPropertyType;
            RuntimePropertyType  = runtimePropertyType;
            PropertyInfo         = propertyInfo;
            ClassType            = JsonClassInfo.GetClassType(runtimePropertyType);
            if (elementType != null)
            {
                Debug.Assert(ClassType == ClassType.Enumerable || ClassType == ClassType.Dictionary);
                ElementClassInfo = options.GetOrAddClass(elementType);
            }

            IsNullableType = runtimePropertyType.IsGenericType && runtimePropertyType.GetGenericTypeDefinition() == typeof(Nullable <>);
            CanBeNull      = IsNullableType || !runtimePropertyType.IsValueType;
        }
Beispiel #11
0
        internal JsonClassInfo(Type type, JsonSerializerOptions options)
        {
            Type      = type;
            ClassType = GetClassType(type);

            CreateObject = options.ClassMaterializerStrategy.CreateConstructor(type);

            // Ignore properties on enumerable.
            if (ClassType == ClassType.Object)
            {
                foreach (PropertyInfo propertyInfo in type.GetProperties(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic))
                {
                    // For now we only support public getters\setters
                    if (propertyInfo.GetMethod?.IsPublic == true ||
                        propertyInfo.SetMethod?.IsPublic == true)
                    {
                        AddProperty(propertyInfo.PropertyType, propertyInfo, type, options);
                    }
                }
            }
            else if (ClassType == ClassType.Enumerable)
            {
                // Add a single property that maps to the class type so we can have policies applied.
                AddProperty(type, propertyInfo: null, type, options);

                // Create a ClassInfo that maps to the element type which is used for (de)serialization and policies.
                Type elementType = GetElementType(type);
                ElementClassInfo = options.GetOrAddClass(elementType);

                GetPolicies(options);
            }
            else
            {
                Debug.Assert(ClassType == ClassType.Value);

                // Add a single property that maps to the class type so we can have policies applied.
                AddProperty(type, null, type, options);
            }
        }
        private static void AddNewStackFrame(
            JsonClassInfo nextClassInfo,
            object nextValue,
            ref WriteObjectState current,
            ref List <WriteObjectState> previous,
            ref int arrayIndex)
        {
            SetPreviousState(ref previous, current, arrayIndex++);
            current.Reset();
            current.ClassInfo    = nextClassInfo;
            current.CurrentValue = nextValue;

            if (nextClassInfo.ClassType == ClassType.Enumerable)
            {
                current.PopStackOnEndArray = true;
                current.PropertyInfo       = current.ClassInfo.GetPolicyProperty();
            }
            else
            {
                Debug.Assert(nextClassInfo.ClassType == ClassType.Object);
                current.PopStackOnEndObject = true;
            }
        }
Beispiel #13
0
        private static void HandleStartObject(JsonSerializerOptions options, ref Utf8JsonReader reader, ref ReadStack state)
        {
            Debug.Assert(!state.Current.IsProcessingDictionary);

            if (state.Current.IsProcessingEnumerable)
            {
                // A nested object within an enumerable.
                Type objType = state.Current.GetElementType();
                state.Push();
                state.Current.Initialize(objType, options);
            }
            else if (state.Current.JsonPropertyInfo != null)
            {
                // Nested object.
                Type objType = state.Current.JsonPropertyInfo.RuntimePropertyType;
                state.Push();
                state.Current.Initialize(objType, options);
            }

            JsonClassInfo classInfo = state.Current.JsonClassInfo;

            state.Current.ReturnValue = classInfo.CreateObject();
        }
Beispiel #14
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.");
            }
        }
Beispiel #15
0
        private static object ReadCore(
            Type returnType,
            JsonSerializerOptions options,
            ref Utf8JsonReader reader)
        {
            if (options == null)
            {
                options = s_defaultSettings;
            }

            ReadStack     state     = default;
            JsonClassInfo classInfo = options.GetOrAddClass(returnType);

            state.Current.JsonClassInfo = classInfo;
            if (classInfo.ClassType != ClassType.Object)
            {
                state.Current.JsonPropertyInfo = classInfo.GetPolicyProperty();
            }

            ReadCore(options, ref reader, ref state);

            return(state.Current.ReturnValue);
        }
Beispiel #16
0
        public static object CreateEnumerableValue(ref Utf8JsonReader reader, ref ReadStack state, JsonSerializerOptions options)
        {
            JsonPropertyInfo jsonPropertyInfo = state.Current.JsonPropertyInfo;

            // If the property has an EnumerableConverter, then we use tempEnumerableValues.
            if (jsonPropertyInfo.EnumerableConverter != null)
            {
                IList converterList;
                if (jsonPropertyInfo.ElementClassInfo.ClassType == ClassType.Value)
                {
                    converterList = jsonPropertyInfo.ElementClassInfo.GetPolicyProperty().CreateConverterList();
                }
                else
                {
                    converterList = new List <object>();
                }

                state.Current.TempEnumerableValues = converterList;

                return(null);
            }

            Type propType = state.Current.JsonPropertyInfo.RuntimePropertyType;

            if (typeof(IList).IsAssignableFrom(propType))
            {
                // If IList, add the members as we create them.
                JsonClassInfo collectionClassInfo = state.Current.JsonPropertyInfo.RuntimeClassInfo;
                IList         collection          = (IList)collectionClassInfo.CreateObject();
                return(collection);
            }
            else
            {
                ThrowHelper.ThrowJsonException_DeserializeUnableToConvertValue(propType, reader, state.JsonPath);
                return(null);
            }
        }
Beispiel #17
0
        internal static object CreateEnumerableValue(ref Utf8JsonReader reader, ref ReadStack state, JsonSerializerOptions options)
        {
            // If the property has an EnumerableConverter, then we use tempEnumerableValues.
            if (state.Current.JsonPropertyInfo.EnumerableConverter != null)
            {
                state.Current.TempEnumerableValues = new List <object>();
                return(null);
            }

            Type propType = state.Current.JsonPropertyInfo.RuntimePropertyType;

            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
            {
                ThrowHelper.ThrowJsonReaderException_DeserializeUnableToConvertValue(propType, reader, state);
                return(null);
            }
        }
Beispiel #18
0
        private static async ValueTask <TValue> ReadAsync <TValue>(Stream utf8Json, Type returnType, JsonSerializerOptions options = null, CancellationToken cancellationToken = default)
        {
            options ??= s_defaultSettings;

            ReadStack     state     = default;
            JsonClassInfo classInfo = options.GetOrAddClass(returnType);

            state.Current.JsonClassInfo = classInfo;
            if (classInfo.ClassType != ClassType.Object)
            {
                state.Current.JsonPropertyInfo = classInfo.GetPolicyProperty();
            }

            var readerState = new JsonReaderState(options.ReaderOptions);

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

            int  bytesInBuffer  = 0;
            long totalBytesRead = 0;
            int  clearMax       = 0;

            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;
                    }

                    // Process the data available
                    ReadCore(
                        ref readerState,
                        isFinalBlock,
                        new Span <byte>(buffer, 0, bytesInBuffer),
                        options,
                        ref state);

                    Debug.Assert(readerState.BytesConsumed <= bytesInBuffer);
                    int bytesConsumed = (int)readerState.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, 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)
            {
                throw new JsonReaderException(
                          SR.Format(SR.DeserializeDataRemaining, totalBytesRead, bytesInBuffer),
                          readerState);
            }

            return((TValue)state.Current.ReturnValue);
        }
Beispiel #19
0
        private static bool HandleEnumerable(
            JsonClassInfo elementClassInfo,
            JsonSerializerOptions options,
            ref Utf8JsonWriter writer,
            ref WriteStack state)
        {
            Debug.Assert(state.Current.JsonPropertyInfo.ClassType == ClassType.Enumerable);

            JsonPropertyInfo jsonPropertyInfo = state.Current.JsonPropertyInfo;

            if (state.Current.Enumerator == null)
            {
                if (jsonPropertyInfo._name == null)
                {
                    writer.WriteStartArray();
                }
                else
                {
                    writer.WriteStartArray(jsonPropertyInfo._name);
                }

                IEnumerable enumerable = (IEnumerable)jsonPropertyInfo.GetValueAsObject(state.Current.CurrentValue, options);

                if (enumerable != null)
                {
                    state.Current.Enumerator = enumerable.GetEnumerator();
                }
            }

            if (state.Current.Enumerator != null && state.Current.Enumerator.MoveNext())
            {
                // If the enumerator contains typeof(object), get the run-time type
                if (elementClassInfo.ClassType == ClassType.Object && jsonPropertyInfo.ElementClassInfo.Type == typeof(object))
                {
                    object currentValue = state.Current.Enumerator.Current;
                    if (currentValue != null)
                    {
                        Type runtimeType = currentValue.GetType();

                        // Ignore object() instances since they are handled as an empty object.
                        if (runtimeType != typeof(object))
                        {
                            elementClassInfo = options.GetOrAddClass(runtimeType);
                        }
                    }
                }

                if (elementClassInfo.ClassType == ClassType.Value)
                {
                    elementClassInfo.GetPolicyProperty().WriteEnumerable(options, ref state.Current, ref writer);
                }
                else if (state.Current.Enumerator.Current == null)
                {
                    // Write a null object or enumerable.
                    writer.WriteNullValue();
                }
                else
                {
                    // An object or another enumerator requires a new stack frame.
                    object nextValue = state.Current.Enumerator.Current;
                    state.Push(elementClassInfo, nextValue);
                }

                return(false);
            }

            // We are done enumerating.
            writer.WriteEndArray();

            if (state.Current.PopStackOnEndArray)
            {
                state.Pop();
            }
            else
            {
                state.Current.EndArray();
            }

            return(true);
        }
        private static bool HandleObject(
            JsonSerializerOptions options,
            Utf8JsonWriter writer,
            ref WriteStack state)
        {
            Debug.Assert(
                state.Current.JsonClassInfo.ClassType == ClassType.Object ||
                state.Current.JsonClassInfo.ClassType == ClassType.Unknown);

            JsonPropertyInfo jsonPropertyInfo = state.Current.JsonClassInfo.GetProperty(state.Current.PropertyIndex);

            bool   obtainedValue = false;
            object currentValue  = null;

            // Check for polymorphism.
            if (jsonPropertyInfo.ClassType == ClassType.Unknown)
            {
                currentValue  = jsonPropertyInfo.GetValueAsObject(state.Current.CurrentValue);
                obtainedValue = true;
                GetRuntimePropertyInfo(currentValue, state.Current.JsonClassInfo, ref jsonPropertyInfo, options);
            }

            state.Current.JsonPropertyInfo = jsonPropertyInfo;

            if (jsonPropertyInfo.ClassType == ClassType.Value)
            {
                jsonPropertyInfo.Write(options, ref state.Current, writer);
                state.Current.NextProperty();
                return(true);
            }

            // A property that returns an enumerator keeps the same stack frame.
            if (jsonPropertyInfo.ClassType == ClassType.Enumerable)
            {
                bool endOfEnumerable = HandleEnumerable(jsonPropertyInfo.ElementClassInfo, options, writer, ref state);
                if (endOfEnumerable)
                {
                    state.Current.NextProperty();
                }

                return(endOfEnumerable);
            }

            // A property that returns a dictionary keeps the same stack frame.
            if (jsonPropertyInfo.ClassType == ClassType.Dictionary)
            {
                bool endOfEnumerable = HandleDictionary(jsonPropertyInfo.ElementClassInfo, options, writer, ref state);
                if (endOfEnumerable)
                {
                    state.Current.NextProperty();
                }

                return(endOfEnumerable);
            }

            // A property that returns an object.
            if (!obtainedValue)
            {
                currentValue = jsonPropertyInfo.GetValueAsObject(state.Current.CurrentValue);
            }

            if (currentValue != null)
            {
                // A new stack frame is required.
                JsonPropertyInfo previousPropertyInfo = state.Current.JsonPropertyInfo;

                state.Current.NextProperty();

                JsonClassInfo nextClassInfo = options.GetOrAddClass(jsonPropertyInfo.RuntimePropertyType);
                state.Push(nextClassInfo, currentValue);

                // Set the PropertyInfo so we can obtain the property name in order to write it.
                state.Current.JsonPropertyInfo = previousPropertyInfo;
            }
            else
            {
                if (!jsonPropertyInfo.IgnoreNullValues)
                {
                    writer.WriteNull(jsonPropertyInfo._escapedName);
                }

                state.Current.NextProperty();
            }

            return(true);
        }
Beispiel #21
0
        private static bool HandleObject(
            JsonSerializerOptions options,
            ref Utf8JsonWriter writer,
            ref WriteStack state)
        {
            Debug.Assert(state.Current.JsonClassInfo.ClassType == ClassType.Object);

            JsonPropertyInfo jsonPropertyInfo  = state.Current.JsonClassInfo.GetProperty(state.Current.PropertyIndex);
            ClassType        propertyClassType = jsonPropertyInfo.ClassType;

            bool   obtainedValue = false;
            object currentValue  = null;

            // Check for polymorphism.
            if (jsonPropertyInfo.RuntimePropertyType == typeof(object))
            {
                Debug.Assert(propertyClassType == ClassType.Object);

                currentValue  = jsonPropertyInfo.GetValueAsObject(state.Current.CurrentValue, options);
                obtainedValue = true;

                if (currentValue != null)
                {
                    Type runtimeType = currentValue.GetType();

                    // Ignore object() instances since they are handled as an empty object.
                    if (runtimeType != typeof(object))
                    {
                        propertyClassType = JsonClassInfo.GetClassType(runtimeType);
                        jsonPropertyInfo  = state.Current.JsonClassInfo.CreatePolymorphicProperty(jsonPropertyInfo, runtimeType, options);
                    }
                }
            }

            state.Current.JsonPropertyInfo = jsonPropertyInfo;

            if (propertyClassType == ClassType.Value)
            {
                jsonPropertyInfo.Write(options, ref state.Current, ref writer);
                state.Current.NextProperty();
                return(true);
            }

            // A property that returns an enumerator keeps the same stack frame.
            if (propertyClassType == ClassType.Enumerable)
            {
                bool endOfEnumerable = HandleEnumerable(jsonPropertyInfo.ElementClassInfo, options, ref writer, ref state);
                if (endOfEnumerable)
                {
                    state.Current.NextProperty();
                }

                return(endOfEnumerable);
            }

            // A property that returns an object.
            if (!obtainedValue)
            {
                currentValue = jsonPropertyInfo.GetValueAsObject(state.Current.CurrentValue, options);
            }

            if (currentValue != null)
            {
                // A new stack frame is required.
                JsonPropertyInfo previousPropertyInfo = state.Current.JsonPropertyInfo;

                state.Current.NextProperty();

                JsonClassInfo nextClassInfo = options.GetOrAddClass(jsonPropertyInfo.RuntimePropertyType);
                state.Push(nextClassInfo, currentValue);

                // Set the PropertyInfo so we can obtain the property name in order to write it.
                state.Current.JsonPropertyInfo = previousPropertyInfo;
            }
            else
            {
                if (!jsonPropertyInfo.IgnoreNullPropertyValueOnWrite(options))
                {
                    writer.WriteNull(jsonPropertyInfo._escapedName);
                }

                state.Current.NextProperty();
            }

            return(true);
        }
Beispiel #22
0
        private void DetermineSerializationCapabilities(JsonSerializerOptions options)
        {
            bool hasIgnoreAttribute = (GetAttribute <JsonIgnoreAttribute>() != null);

            if (hasIgnoreAttribute)
            {
                // We don't serialize or deserialize.
                return;
            }

            if (ClassType != ClassType.Enumerable)
            {
                // We serialize if there is a getter + no [Ignore] attribute + not ignoring readonly properties.
                ShouldSerialize = HasGetter && (HasSetter || !options.IgnoreReadOnlyProperties);

                // We deserialize if there is a setter + no [Ignore] attribute.
                ShouldDeserialize = HasSetter;
            }
            else
            {
                if (HasGetter)
                {
                    if (HasSetter)
                    {
                        ShouldDeserialize = true;
                    }
                    else if (RuntimePropertyType.IsAssignableFrom(typeof(IList)))
                    {
                        ShouldDeserialize = true;
                    }
                    //else
                    //{
                    //    // todo: future feature that allows non-List types (e.g. from System.Collections.Immutable) to have converters.
                    //}
                }
                //else if (HasSetter)
                //{
                //    // todo: Special case where there is no getter but a setter (and an EnumerableConverter)
                //}

                if (ShouldDeserialize)
                {
                    ShouldSerialize = HasGetter;

                    if (RuntimePropertyType.IsArray)
                    {
                        EnumerableConverter = s_jsonArrayConverter;
                    }
                    else if (typeof(IEnumerable).IsAssignableFrom(RuntimePropertyType))
                    {
                        Type elementType = JsonClassInfo.GetElementType(RuntimePropertyType);

                        // If the property type only has interface(s) exposed by JsonEnumerableT<T> then use JsonEnumerableT as the converter.
                        if (RuntimePropertyType.IsAssignableFrom(typeof(JsonEnumerableT <>).MakeGenericType(elementType)))
                        {
                            EnumerableConverter = s_jsonEnumerableConverter;
                        }
                    }
                }
                else
                {
                    ShouldSerialize = HasGetter && !options.IgnoreReadOnlyProperties;
                }
            }
        }
Beispiel #23
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");
        }
        private static bool HandleEnumerable(
            JsonClassInfo elementClassInfo,
            JsonSerializerOptions options,
            ref Utf8JsonWriter writer,
            ref WriteStack state)
        {
            Debug.Assert(state.Current.JsonPropertyInfo.ClassType == ClassType.Enumerable);

            JsonPropertyInfo jsonPropertyInfo = state.Current.JsonPropertyInfo;

            if (state.Current.Enumerator == null)
            {
                if (jsonPropertyInfo._name == null)
                {
                    writer.WriteStartArray();
                }
                else
                {
                    writer.WriteStartArray(jsonPropertyInfo._name);
                }

                IEnumerable enumerable = (IEnumerable)jsonPropertyInfo.GetValueAsObject(state.Current.CurrentValue, options);

                if (enumerable != null)
                {
                    state.Current.Enumerator = enumerable.GetEnumerator();
                }
            }

            if (state.Current.Enumerator != null && state.Current.Enumerator.MoveNext())
            {
                // Check for polymorphism.
                if (elementClassInfo.ClassType == ClassType.Unknown)
                {
                    object currentValue = state.Current.Enumerator.Current;
                    GetRuntimeClassInfo(currentValue, ref elementClassInfo, options);
                }

                if (elementClassInfo.ClassType == ClassType.Value)
                {
                    elementClassInfo.GetPolicyProperty().WriteEnumerable(options, ref state.Current, ref writer);
                }
                else if (state.Current.Enumerator.Current == null)
                {
                    // Write a null object or enumerable.
                    writer.WriteNullValue();
                }
                else
                {
                    // An object or another enumerator requires a new stack frame.
                    object nextValue = state.Current.Enumerator.Current;
                    state.Push(elementClassInfo, nextValue);
                }

                return(false);
            }

            // We are done enumerating.
            writer.WriteEndArray();

            if (state.Current.PopStackOnEndArray)
            {
                state.Pop();
            }
            else
            {
                state.Current.EndArray();
            }

            return(true);
        }
        private static bool HandleEnumerable(
            JsonClassInfo elementClassInfo,
            JsonSerializerOptions options,
            ref Utf8JsonWriter writer,
            ref WriteObjectState current,
            ref List <WriteObjectState> previous,
            ref int arrayIndex)
        {
            Debug.Assert(current.PropertyInfo.ClassType == ClassType.Enumerable);

            JsonPropertyInfo propertyInfo = current.PropertyInfo;

            if (current.Enumerator == null)
            {
                if (propertyInfo.Name == null)
                {
                    writer.WriteStartArray();
                }
                else
                {
                    writer.WriteStartArray(propertyInfo.Name);
                }

                IEnumerable enumerable = (IEnumerable)propertyInfo.GetValueAsObject(current.CurrentValue, options);

                if (enumerable != null)
                {
                    current.Enumerator = enumerable.GetEnumerator();
                }
            }

            if (current.Enumerator != null && current.Enumerator.MoveNext())
            {
                if (elementClassInfo.ClassType == ClassType.Value)
                {
                    propertyInfo.Write(options, ref current, ref writer);
                }
                else
                {
                    // An object or another enumerator requires a new stack frame
                    JsonClassInfo nextClassInfo = propertyInfo.ElementClassInfo;
                    object        nextValue     = current.Enumerator.Current;
                    AddNewStackFrame(nextClassInfo, nextValue, ref current, ref previous, ref arrayIndex);
                }

                return(false);
            }

            // We are done enumerating.
            writer.WriteEndArray();

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

            return(true);
        }
Beispiel #26
0
 public void Initialize(Type type, JsonSerializerOptions options)
 {
     JsonClassInfo = options.GetOrAddClass(type);
     InitializeJsonPropertyInfo();
 }
        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.PropertyInitialized)
            {
                state.Push();
                state.Current.JsonClassInfo = jsonPropertyInfo.ElementClassInfo;
                state.Current.InitializeJsonPropertyInfo();
                state.Current.PropertyInitialized = 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.IsProcessingImmutableDictionary)
                {
                    state.Current.TempDictionaryValues = (IDictionary)classInfo.CreateObject();
                }
                else
                {
                    state.Current.ReturnValue = classInfo.CreateObject();
                }
                return;
            }

            state.Current.PropertyInitialized = true;

            if (state.Current.IsProcessingImmutableDictionary)
            {
                JsonClassInfo dictionaryClassInfo = options.GetOrAddClass(jsonPropertyInfo.RuntimePropertyType);
                state.Current.TempDictionaryValues = (IDictionary)dictionaryClassInfo.CreateObject();
            }
            // Else if current property is already set (from a constructor, for example), leave as-is.
            else if (jsonPropertyInfo.GetValueAsObject(state.Current.ReturnValue) == null)
            {
                // 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);
                    }
                }
            }
        }
        private static bool HandleDictionary(
            JsonClassInfo elementClassInfo,
            JsonSerializerOptions options,
            Utf8JsonWriter writer,
            ref WriteStack state)
        {
            JsonPropertyInfo jsonPropertyInfo = state.Current.JsonPropertyInfo;

            if (state.Current.Enumerator == null)
            {
                IEnumerable enumerable;

                enumerable = (IEnumerable)jsonPropertyInfo.GetValueAsObject(state.Current.CurrentValue);
                if (enumerable == null)
                {
                    // Write a null object or enumerable.
                    state.Current.WriteObjectOrArrayStart(ClassType.Dictionary, writer, writeNull: true);
                    return(true);
                }

                state.Current.Enumerator = enumerable.GetEnumerator();
                state.Current.WriteObjectOrArrayStart(ClassType.Dictionary, writer);
            }

            if (state.Current.Enumerator.MoveNext())
            {
                // Handle DataExtension.
                if (ReferenceEquals(jsonPropertyInfo, state.Current.JsonClassInfo.DataExtensionProperty))
                {
                    WriteExtensionData(writer, ref state.Current);
                }
                else
                {
                    // Check for polymorphism.
                    if (elementClassInfo.ClassType == ClassType.Unknown)
                    {
                        object currentValue = ((IDictionaryEnumerator)state.Current.Enumerator).Entry.Value;
                        GetRuntimeClassInfo(currentValue, ref elementClassInfo, options);
                    }

                    if (elementClassInfo.ClassType == ClassType.Value)
                    {
                        elementClassInfo.GetPolicyProperty().WriteDictionary(ref state.Current, writer);
                    }
                    else if (state.Current.Enumerator.Current == null)
                    {
                        writer.WriteNull(jsonPropertyInfo.Name);
                    }
                    else
                    {
                        // An object or another enumerator requires a new stack frame.
                        var    enumerator = (IDictionaryEnumerator)state.Current.Enumerator;
                        object value      = enumerator.Value;
                        state.Push(elementClassInfo, value);
                        state.Current.KeyName = (string)enumerator.Key;
                    }
                }

                return(false);
            }

            // We are done enumerating.
            writer.WriteEndObject();

            if (state.Current.PopStackOnEnd)
            {
                state.Pop();
            }
            else
            {
                state.Current.EndDictionary();
            }

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

            if (state.Current.IsProcessingEnumerable)
            {
                Type objType = state.Current.GetElementType();
                state.Push();
                state.Current.Initialize(objType, options);
            }
            else if (state.Current.JsonPropertyInfo != null)
            {
                if (state.Current.IsDictionary)
                {
                    // Verify that the Dictionary can be deserialized by having <string> as first generic argument.
                    Type[] args = state.Current.JsonClassInfo.Type.GetGenericArguments();
                    if (args.Length == 0 || args[0].UnderlyingSystemType != typeof(string))
                    {
                        ThrowHelper.ThrowJsonException_DeserializeUnableToConvertValue(state.Current.JsonClassInfo.Type, reader, state.PropertyPath);
                    }

                    if (state.Current.ReturnValue == null)
                    {
                        // The Dictionary created below will be returned to corresponding Parse() etc method.
                        // Ensure any nested array creates a new frame.
                        state.Current.EnumerableCreated = true;
                    }
                    else
                    {
                        ClassType classType = state.Current.JsonClassInfo.ElementClassInfo.ClassType;

                        // Verify that the second parameter is not a value.
                        if (state.Current.JsonClassInfo.ElementClassInfo.ClassType == ClassType.Value)
                        {
                            ThrowHelper.ThrowJsonException_DeserializeUnableToConvertValue(state.Current.JsonClassInfo.Type, reader, state.PropertyPath);
                        }

                        // A nested object, dictionary or enumerable.
                        JsonClassInfo classInfoTemp = state.Current.JsonClassInfo;
                        state.Push();
                        state.Current.JsonClassInfo = classInfoTemp.ElementClassInfo;
                        state.Current.InitializeJsonPropertyInfo();
                    }
                }
                else
                {
                    // Nested object.
                    Type objType = state.Current.JsonPropertyInfo.RuntimePropertyType;
                    state.Push();
                    state.Current.Initialize(objType, options);
                }
            }

            JsonClassInfo classInfo = state.Current.JsonClassInfo;

            state.Current.ReturnValue = classInfo.CreateObject();
        }
Beispiel #30
0
        private void DetermineSerializationCapabilities(JsonSerializerOptions options)
        {
            if (ClassType != ClassType.Enumerable && ClassType != ClassType.Dictionary)
            {
                // We serialize if there is a getter + not ignoring readonly properties.
                ShouldSerialize = HasGetter && (HasSetter || !options.IgnoreReadOnlyProperties);

                // We deserialize if there is a setter.
                ShouldDeserialize = HasSetter;
            }
            else
            {
                if (HasGetter)
                {
                    if (HasSetter)
                    {
                        ShouldDeserialize = true;
                    }
                    else if (!RuntimePropertyType.IsArray &&
                             (typeof(IList).IsAssignableFrom(RuntimePropertyType) || typeof(IDictionary).IsAssignableFrom(RuntimePropertyType)))
                    {
                        ShouldDeserialize = true;
                    }
                }
                //else if (HasSetter)
                //{
                //    // todo: Special case where there is no getter but a setter (and an EnumerableConverter)
                //}

                if (ShouldDeserialize)
                {
                    ShouldSerialize = HasGetter;

                    if (RuntimePropertyType.IsArray)
                    {
                        EnumerableConverter = s_jsonArrayConverter;
                    }
                    else if (typeof(IEnumerable).IsAssignableFrom(RuntimePropertyType))
                    {
                        Type elementType = JsonClassInfo.GetElementType(RuntimePropertyType, ParentClassType, PropertyInfo);

                        // If the property type only has interface(s) exposed by JsonEnumerableT<T> then use JsonEnumerableT as the converter.
                        if (RuntimePropertyType.IsAssignableFrom(typeof(JsonEnumerableT <>).MakeGenericType(elementType)))
                        {
                            EnumerableConverter = s_jsonEnumerableConverter;
                        }
                        // Else if IList can't be assigned from the property type (we populate and return an IList directly)
                        // and the type can be constructed with an IEnumerable<T>, then use the
                        // IEnumerableConstructible converter to create the instance.
                        else if (!typeof(IList).IsAssignableFrom(RuntimePropertyType) &&
                                 RuntimePropertyType.GetConstructor(new Type[] { typeof(List <>).MakeGenericType(elementType) }) != null)
                        {
                            EnumerableConverter = s_jsonIEnumerableConstuctibleConverter;
                        }
                        // Else if it's a System.Collections.Immutable type with one generic argument.
                        else if (RuntimePropertyType.IsGenericType &&
                                 RuntimePropertyType.FullName.StartsWith(DefaultImmutableConverter.ImmutableNamespace) &&
                                 RuntimePropertyType.GetGenericArguments().Length == 1)
                        {
                            EnumerableConverter = s_jsonImmutableConverter;
                            ((DefaultImmutableConverter)EnumerableConverter).RegisterImmutableCollectionType(RuntimePropertyType, elementType, options);
                        }
                    }
                }
                else
                {
                    ShouldSerialize = HasGetter && !options.IgnoreReadOnlyProperties;
                }
            }
        }