Exemple #1
0
        private static async Task WriteAsyncCore(object value, Type type, PipeWriter utf8Json, JsonSerializerOptions options, CancellationToken cancellationToken)
        {
            if (options == null)
            {
                options = s_defaultSettings;
            }

            var writerState = new JsonWriterState(options.WriterOptions);

            // Allocate the initial buffer. We don't want to use the existing buffer as there may be very few bytes left
            // and we won't be able to calculate flushThreshold appropriately.
            Memory <byte> memory = utf8Json.GetMemory(options.EffectiveBufferSize);

            if (value == null)
            {
                WriteNull(ref writerState, utf8Json);
                await utf8Json.FlushAsync(cancellationToken).ConfigureAwait(false);

                return;
            }

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

            JsonClassInfo    classInfo = options.GetOrAddClass(type);
            WriteObjectState current   = default;

            current.ClassInfo    = classInfo;
            current.CurrentValue = value;
            if (classInfo.ClassType != ClassType.Object)
            {
                current.PropertyInfo = classInfo.GetPolicyProperty();
            }
            List <WriteObjectState> previous = null;
            int arrayIndex = 0;

            bool isFinalBlock;

            // For Pipes there is not a way to get current buffer total size, so we just use the initial memory size.
            int flushThreshold = (int)(memory.Length * .9); //todo: determine best value here (extensible?)

            do
            {
                isFinalBlock = Write(ref writerState, utf8Json, flushThreshold, options, ref current, ref previous, ref arrayIndex);
                await utf8Json.FlushAsync(cancellationToken).ConfigureAwait(false);
            } while (!isFinalBlock);
        }
Exemple #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);
        }
Exemple #3
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 ProcessMissingProperty(
            ReadOnlySpan <byte> unescapedPropertyName,
            JsonSerializerOptions options,
            ref Utf8JsonReader reader,
            ref ReadStack state)
        {
            JsonPropertyInfo jsonPropertyInfo = state.Current.JsonClassInfo.DataExtensionProperty;

            Debug.Assert(jsonPropertyInfo != null);
            Debug.Assert(state.Current.ReturnValue != null);

            IDictionary extensionData = (IDictionary)jsonPropertyInfo.GetValueAsObject(state.Current.ReturnValue);

            if (extensionData == null)
            {
                Type type = jsonPropertyInfo.DeclaredPropertyType;

                // Create the appropriate dictionary type. We already verified the types.
                Debug.Assert(type.IsGenericType);
                Debug.Assert(type.GetGenericArguments().Length == 2);
                Debug.Assert(type.GetGenericArguments()[0].UnderlyingSystemType == typeof(string));
                Debug.Assert(
                    type.GetGenericArguments()[1].UnderlyingSystemType == typeof(object) ||
                    type.GetGenericArguments()[1].UnderlyingSystemType == typeof(JsonElement));

                extensionData = (IDictionary)options.GetOrAddClass(type).CreateObject();
                jsonPropertyInfo.SetValueAsObject(state.Current.ReturnValue, extensionData);
            }

            JsonElement jsonElement;

            using (JsonDocument jsonDocument = JsonDocument.ParseValue(ref reader))
            {
                jsonElement = jsonDocument.RootElement.Clone();
            }

            string keyName = JsonHelpers.Utf8GetString(unescapedPropertyName);

            // Currently we don't apply any naming policy. If we do, we'd have to pass it onto the JsonDocument.

            extensionData.Add(keyName, jsonElement);
        }
Exemple #5
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;
        }
Exemple #6
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);
            }
            else if (ClassType == ClassType.Value)
            {
                // Add a single property that maps to the class type so we can have policies applied.
                AddProperty(type, propertyInfo: null, type, options);
            }
            else
            {
                Debug.Assert(ClassType == ClassType.Unknown);
                // Do nothing. The type is typeof(object).
            }
        }
Exemple #7
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;
        }
Exemple #8
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);
        }
Exemple #9
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.");
            }
        }
Exemple #10
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 = options.GetOrAddClass(propType);
                IList         collection          = (IList)collectionClassInfo.CreateObject();
                return(collection);
            }
            else
            {
                ThrowHelper.ThrowJsonException_DeserializeUnableToConvertValue(propType, reader, state.PropertyPath);
                return(null);
            }
        }
Exemple #11
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.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
            {
                ThrowHelper.ThrowJsonReaderException_DeserializeUnableToConvertValue(propType, reader, state);
                return(null);
            }
        }
        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);
                    }
                }
            }
        }
Exemple #13
0
 public void Initialize(Type type, JsonSerializerOptions options)
 {
     JsonClassInfo = options.GetOrAddClass(type);
     InitializeJsonPropertyInfo();
 }
        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);
        }
Exemple #15
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");
        }
Exemple #16
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);
        }
Exemple #17
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);
        }
Exemple #18
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);
        }
Exemple #19
0
        internal JsonClassInfo(Type type, JsonSerializerOptions options)
        {
            Type      = type;
            ClassType = GetClassType(type);

            CreateObject = options.ClassMaterializerStrategy.CreateConstructor(type);

            // Ignore properties on enumerable.
            switch (ClassType)
            {
            case ClassType.Object:
                var propertyNames = new HashSet <string>(StringComparer.Ordinal);

                foreach (PropertyInfo propertyInfo in type.GetProperties(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic))
                {
                    // Ignore indexers
                    if (propertyInfo.GetIndexParameters().Length > 0)
                    {
                        continue;
                    }

                    // For now we only support public getters\setters
                    if (propertyInfo.GetMethod?.IsPublic == true ||
                        propertyInfo.SetMethod?.IsPublic == true)
                    {
                        JsonPropertyInfo jsonPropertyInfo = AddProperty(propertyInfo.PropertyType, propertyInfo, type, options);

                        Debug.Assert(jsonPropertyInfo.NameUsedToCompareAsString != null);

                        // If the JsonPropertyNameAttribute or naming policy results in collisions, throw an exception.
                        if (!propertyNames.Add(jsonPropertyInfo.NameUsedToCompareAsString))
                        {
                            ThrowHelper.ThrowInvalidOperationException_SerializerPropertyNameConflict(this, jsonPropertyInfo);
                        }

                        jsonPropertyInfo.ClearUnusedValuesAfterAdd();
                    }
                }

                DetermineExtensionDataProperty();
                break;

            case ClassType.Enumerable:
            case ClassType.Dictionary:
            {
                // Add a single property that maps to the class type so we can have policies applied.
                JsonPropertyInfo policyProperty = AddPolicyProperty(type, options);

                // Use the type from the property policy to get any late-bound concrete types (from an interface like IDictionary).
                CreateObject = options.ClassMaterializerStrategy.CreateConstructor(policyProperty.RuntimePropertyType);

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

            case ClassType.ImmutableDictionary:
            {
                // Add a single property that maps to the class type so we can have policies applied.
                AddPolicyProperty(type, options);

                Type elementType = GetElementType(type, parentType: null, memberInfo: null);

                CreateObject = options.ClassMaterializerStrategy.CreateConstructor(
                    typeof(Dictionary <,>).MakeGenericType(typeof(string), elementType));

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

            case ClassType.Value:
            case ClassType.Unknown:
                // Add a single property that maps to the class type so we can have policies applied.
                AddPolicyProperty(type, options);
                break;

            default:
                Debug.Fail($"Unexpected class type: {ClassType}");
                break;
            }
        }