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

            Type arrayType = state.Current.JsonPropertyInfo.PropertyType;

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

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

                    state.Push();

                    state.Current.JsonClassInfo      = options.GetOrAddClass(elementType);
                    state.Current.JsonPropertyInfo   = state.Current.JsonClassInfo.GetPolicyProperty();
                    state.Current.PopStackOnEndArray = true;
                }
                else
                {
                    state.Current.EnumerableCreated = true;
                }

                // If current property is already set (from a constructor, for example) leave as-is
                if (state.Current.JsonPropertyInfo.GetValueAsObject(state.Current.ReturnValue, options) == null)
                {
                    // Create the enumerable.
                    object value = ReadStackFrame.CreateEnumerableValue(ref reader, ref state, options);
                    if (value != null)
                    {
                        if (state.Current.ReturnValue != null)
                        {
                            state.Current.JsonPropertyInfo.SetValueAsObject(state.Current.ReturnValue, value, options);
                        }
                        else
                        {
                            // Primitive arrays being returned without object
                            state.Current.SetReturnValue(value, options);
                        }
                    }
                }
            }
        }
        internal override void ReadEnumerable(JsonTokenType tokenType, JsonSerializerOptions options, ref ReadStack state, ref Utf8JsonReader reader)
        {
            if (ValueConverter != null)
            {
                if (ValueConverter.TryRead(RuntimePropertyType, ref reader, out TRuntimeProperty value))
                {
                    ReadStackFrame.SetReturnValue(value, options, ref state.Current);
                    return;
                }
            }

            ThrowHelper.ThrowJsonReaderException_DeserializeUnableToConvertValue(RuntimePropertyType, reader, state);
        }
Esempio n. 3
0
        internal void UpdateSortedPropertyCache(ref ReadStackFrame frame)
        {
            // Todo: on classes with many properties (about 50) we need to switch to a hashtable for performance.
            // Todo: when using PropertyNameCaseInsensitive we also need to use the hashtable with case-insensitive
            // comparison to handle Turkish etc. cultures properly.

            Debug.Assert(_propertyRefs != null);

            // Set the sorted property cache. Overwrite any existing cache which can occur in multi-threaded cases.
            if (frame.PropertyRefCache != null)
            {
                List <PropertyRef> cache = frame.PropertyRefCache;

                // Add any missing properties. This creates a consistent cache count which is important for
                // the loop in GetProperty() when there are multiple threads in a race conditions each generating
                // a cache for a given POCO type but with different property counts in the json.
                while (cache.Count < _propertyRefs.Count)
                {
                    for (int iProperty = 0; iProperty < _propertyRefs.Count; iProperty++)
                    {
                        PropertyRef propertyRef    = _propertyRefs[iProperty];
                        bool        found          = false;
                        int         iCacheProperty = 0;

                        for (; iCacheProperty < cache.Count; iCacheProperty++)
                        {
                            if (IsPropertyRefEqual(ref propertyRef, cache[iCacheProperty]))
                            {
                                // The property is already cached, skip to the next property.
                                found = true;
                                break;
                            }
                        }

                        if (found == false)
                        {
                            cache.Add(propertyRef);
                            break;
                        }
                    }
                }

                Debug.Assert(cache.Count == _propertyRefs.Count);
                _propertyRefsSorted    = cache.ToArray();
                frame.PropertyRefCache = null;
            }
        }
Esempio n. 4
0
 // If this method is changed, also change ApplyObjectToEnumerable.
 internal static void ApplyValueToEnumerable <TProperty>(
     ref TProperty value,
     JsonSerializerOptions options,
     ref ReadStackFrame frame)
 {
     if (frame.IsEnumerable())
     {
         if (frame.TempEnumerableValues != null)
         {
             ((IList <TProperty>)frame.TempEnumerableValues).Add(value);
         }
         else
         {
             ((IList <TProperty>)frame.ReturnValue).Add(value);
         }
     }
     else if (frame.IsPropertyEnumerable())
     {
         Debug.Assert(frame.JsonPropertyInfo != null);
         Debug.Assert(frame.ReturnValue != null);
         if (frame.TempEnumerableValues != null)
         {
             ((IList <TProperty>)frame.TempEnumerableValues).Add(value);
         }
         else
         {
             ((IList <TProperty>)frame.JsonPropertyInfo.GetValueAsObject(frame.ReturnValue, options)).Add(value);
         }
     }
     else if (frame.IsDictionary())
     {
         // todo: use TryAdd and throw JsonReaderException
         ((IDictionary <string, TProperty>)frame.ReturnValue).Add(frame.KeyName, value);
     }
     else if (frame.IsPropertyADictionary())
     {
         Debug.Assert(frame.JsonPropertyInfo != null);
         Debug.Assert(frame.ReturnValue != null);
         ((IDictionary <string, TProperty>)frame.JsonPropertyInfo.GetValueAsObject(frame.ReturnValue, options)).Add(frame.KeyName, value);
     }
     else
     {
         Debug.Assert(frame.JsonPropertyInfo != null);
         frame.JsonPropertyInfo.SetValueAsObject(frame.ReturnValue, value, options);
     }
 }
Esempio n. 5
0
        internal override void ReadEnumerable(JsonTokenType tokenType, JsonSerializerOptions options, ref ReadStack state, ref Utf8JsonReader reader)
        {
            if (ValueConverter != null)
            {
                Type propertyType = PropertyType;
                if (propertyType.IsGenericType && propertyType.GetGenericTypeDefinition() == typeof(Nullable <>))
                {
                    propertyType = Nullable.GetUnderlyingType(propertyType);
                }

                if (ValueConverter.TryRead(propertyType, ref reader, out TProperty value))
                {
                    ReadStackFrame.SetReturnValue(value, options, ref state.Current);
                    return;
                }
            }

            ThrowHelper.ThrowJsonReaderException_DeserializeUnableToConvertValue(PropertyType, reader, state);
        }
        private static bool HandleNull(ref Utf8JsonReader reader, ref ReadStack state, JsonSerializerOptions options)
        {
            if (state.Current.Skip())
            {
                return(false);
            }

            // If we don't have a valid property, that means we read "null" for a root object so just return.
            if (state.Current.JsonPropertyInfo == null)
            {
                Debug.Assert(state.IsLastFrame);
                Debug.Assert(state.Current.ReturnValue == null);
                return(true);
            }

            JsonPropertyInfo propertyInfo = state.Current.JsonPropertyInfo;

            if (!propertyInfo.CanBeNull)
            {
                ThrowHelper.ThrowJsonReaderException_DeserializeCannotBeNull(reader, state);
            }

            if (state.Current.IsEnumerable() || state.Current.IsPropertyEnumerable())
            {
                ReadStackFrame.SetReturnValue(null, options, ref state.Current);
                return(false);
            }

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

            if (!propertyInfo.IgnoreNullValues)
            {
                state.Current.JsonPropertyInfo.SetValueAsObject(state.Current.ReturnValue, null, options);
            }

            return(false);
        }
Esempio n. 7
0
        public void Push()
        {
            if (_previous == null)
            {
                _previous = new List <ReadStackFrame>();
            }

            if (_index == _previous.Count)
            {
                // Need to allocate a new array element.
                _previous.Add(Current);
            }
            else
            {
                Debug.Assert(_index < _previous.Count);

                // Use a previously allocated slot.
                Current = _previous[_index];
            }

            Current.Reset();
            _index++;
        }
Esempio n. 8
0
        private static bool HandleEndArray(
            JsonSerializerOptions options,
            ref ReadStack state)
        {
            bool lastFrame = state.IsLastFrame;

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

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

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

            bool setPropertyDirectly;

            if (state.Current.TempEnumerableValues != null)
            {
                JsonEnumerableConverter converter = state.Current.JsonPropertyInfo.EnumerableConverter;
                Debug.Assert(converter != null);

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

            bool valueReturning = state.Current.PopStackOnEndArray;

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

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

            ApplyObjectToEnumerable(value, options, ref state.Current, setPropertyDirectly: setPropertyDirectly);

            if (!valueReturning)
            {
                state.Current.ResetProperty();
            }

            return(false);
        }
Esempio n. 9
0
 // If this method is changed, also change ApplyObjectToEnumerable.
 internal static void ApplyValueToEnumerable <TProperty>(ref TProperty value, JsonSerializerOptions options, ref ReadStackFrame frame)
 {
     if (frame.IsEnumerable())
     {
         if (frame.TempEnumerableValues != null)
         {
             ((IList <TProperty>)frame.TempEnumerableValues).Add(value);
         }
         else
         {
             ((IList <TProperty>)frame.ReturnValue).Add(value);
         }
     }
     else if (frame.IsPropertyEnumerable())
     {
         Debug.Assert(frame.JsonPropertyInfo != null);
         Debug.Assert(frame.ReturnValue != null);
         if (frame.TempEnumerableValues != null)
         {
             ((IList <TProperty>)frame.TempEnumerableValues).Add(value);
         }
         else
         {
             ((IList <TProperty>)frame.JsonPropertyInfo.GetValueAsObject(frame.ReturnValue, options)).Add(value);
         }
     }
     else
     {
         Debug.Assert(frame.JsonPropertyInfo != null);
         frame.JsonPropertyInfo.SetValueAsObject(frame.ReturnValue, value, options);
     }
 }
Esempio n. 10
0
 // If this method is changed, also change ApplyValueToEnumerable.
 internal static void ApplyObjectToEnumerable(object value, JsonSerializerOptions options, ref ReadStackFrame frame, bool setPropertyDirectly = false)
 {
     if (frame.IsEnumerable())
     {
         if (frame.TempEnumerableValues != null)
         {
             frame.TempEnumerableValues.Add(value);
         }
         else
         {
             ((IList)frame.ReturnValue).Add(value);
         }
     }
     else if (!setPropertyDirectly && frame.IsPropertyEnumerable())
     {
         Debug.Assert(frame.JsonPropertyInfo != null);
         Debug.Assert(frame.ReturnValue != null);
         if (frame.TempEnumerableValues != null)
         {
             frame.TempEnumerableValues.Add(value);
         }
         else
         {
             ((IList)frame.JsonPropertyInfo.GetValueAsObject(frame.ReturnValue, options)).Add(value);
         }
     }
     else
     {
         Debug.Assert(frame.JsonPropertyInfo != null);
         frame.JsonPropertyInfo.SetValueAsObject(frame.ReturnValue, value, options);
     }
 }
        private static void HandleStartArray(
            JsonSerializerOptions options,
            ref Utf8JsonReader reader,
            ref ReadStack state)
        {
            JsonPropertyInfo jsonPropertyInfo;

            jsonPropertyInfo = state.Current.JsonPropertyInfo;

            bool skip = jsonPropertyInfo != null && !jsonPropertyInfo.ShouldDeserialize;

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

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

            Type arrayType = jsonPropertyInfo.RuntimePropertyType;

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

            Debug.Assert(state.Current.IsPropertyEnumerable || state.Current.IsDictionary);

            if (state.Current.EnumerableCreated)
            {
                // A nested json array so push a new stack frame.
                Type elementType = state.Current.JsonClassInfo.ElementClassInfo.GetPolicyProperty().RuntimePropertyType;

                state.Push();

                state.Current.Initialize(elementType, options);
                state.Current.PopStackOnEnd = true;
            }
            else
            {
                state.Current.EnumerableCreated = true;
            }

            jsonPropertyInfo = state.Current.JsonPropertyInfo;

            // If current property is already set (from a constructor, for example) leave as-is.
            if (jsonPropertyInfo.GetValueAsObject(state.Current.ReturnValue) == null)
            {
                // Create the enumerable.
                object value = ReadStackFrame.CreateEnumerableValue(ref reader, ref state, options);
                if (value != null)
                {
                    if (state.Current.ReturnValue != null)
                    {
                        state.Current.JsonPropertyInfo.SetValueAsObject(state.Current.ReturnValue, value);
                    }
                    else
                    {
                        // Primitive arrays being returned without object
                        state.Current.SetReturnValue(value);
                    }
                }
            }
        }
Esempio n. 12
0
        internal JsonPropertyInfo GetProperty(ReadOnlySpan <byte> propertyName, ref ReadStackFrame frame)
        {
            ulong            key  = GetKey(propertyName);
            JsonPropertyInfo info = null;

            // First try sorted lookup.
            int propertyIndex = frame.PropertyIndex;

            // If we're not trying to build the cache locally, and there is an existing cache, then use it.
            bool hasPropertyCache = frame.PropertyRefCache == null && _propertyRefsSorted != null;

            if (hasPropertyCache)
            {
                // This .Length is consistent no matter what json data intialized _propertyRefsSorted.
                int count = _propertyRefsSorted.Length;
                if (count != 0)
                {
                    int iForward  = propertyIndex;
                    int iBackward = propertyIndex - 1;
                    while (iForward < count || iBackward >= 0)
                    {
                        if (iForward < count)
                        {
                            if (TryIsPropertyRefEqual(ref _propertyRefsSorted[iForward], propertyName, key, ref info))
                            {
                                return(info);
                            }
                            ++iForward;
                        }

                        if (iBackward >= 0)
                        {
                            if (TryIsPropertyRefEqual(ref _propertyRefsSorted[iBackward], propertyName, key, ref info))
                            {
                                return(info);
                            }
                            --iBackward;
                        }
                    }
                }
            }

            // Try the main list which has all of the properties in a consistent order.
            // We could get here even when hasPropertyCache==true if there is a race condition with different json
            // property ordering and _propertyRefsSorted is re-assigned while in the loop above.
            for (int i = 0; i < _propertyRefs.Count; i++)
            {
                PropertyRef propertyRef = _propertyRefs[i];
                if (TryIsPropertyRefEqual(ref propertyRef, propertyName, key, ref info))
                {
                    break;
                }
            }

            if (!hasPropertyCache)
            {
                if (propertyIndex == 0)
                {
                    // Create the temporary list on first property access to prevent a partially filled List.
                    Debug.Assert(frame.PropertyRefCache == null);
                    frame.PropertyRefCache = new List <PropertyRef>();
                }

                if (info != null)
                {
                    Debug.Assert(frame.PropertyRefCache != null);
                    frame.PropertyRefCache.Add(new PropertyRef(key, info));
                }
            }

            return(info);
        }
Esempio n. 13
0
        private static bool HandleEndArray(
            JsonSerializerOptions options,
            ref Utf8JsonReader reader,
            ref ReadStack state)
        {
            bool lastFrame = state.IsLastFrame;

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

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

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

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

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

            ApplyObjectToEnumerable(value, ref state, ref reader);

            return(false);
        }
Esempio n. 14
0
        internal JsonPropertyInfo GetProperty(JsonSerializerOptions options, ReadOnlySpan <byte> propertyName, ref ReadStackFrame frame)
        {
            // If we should compare with case-insensitive, normalize to an uppercase format since that is what is cached on the propertyInfo.
            if (options.PropertyNameCaseInsensitive)
            {
                string utf16PropertyName = JsonHelpers.Utf8GetString(propertyName);
                string upper             = utf16PropertyName.ToUpperInvariant();
                propertyName = Encoding.UTF8.GetBytes(upper);
            }

            ulong            key  = GetKey(propertyName);
            JsonPropertyInfo info = null;

            // First try sorted lookup.
            int propertyIndex = frame.PropertyIndex;

            // If we're not trying to build the cache locally, and there is an existing cache, then use it.
            bool hasPropertyCache = frame.PropertyRefCache == null && _propertyRefsSorted != null;

            if (hasPropertyCache)
            {
                // This .Length is consistent no matter what json data intialized _propertyRefsSorted.
                int count = _propertyRefsSorted.Length;
                if (count != 0)
                {
                    int iForward  = propertyIndex;
                    int iBackward = propertyIndex - 1;
                    while (iForward < count || iBackward >= 0)
                    {
                        if (iForward < count)
                        {
                            if (TryIsPropertyRefEqual(ref _propertyRefsSorted[iForward], propertyName, key, ref info))
                            {
                                return(info);
                            }
                            ++iForward;
                        }

                        if (iBackward >= 0)
                        {
                            if (TryIsPropertyRefEqual(ref _propertyRefsSorted[iBackward], propertyName, key, ref info))
                            {
                                return(info);
                            }
                            --iBackward;
                        }
                    }
                }
            }

            // Try the main list which has all of the properties in a consistent order.
            // We could get here even when hasPropertyCache==true if there is a race condition with different json
            // property ordering and _propertyRefsSorted is re-assigned while in the loop above.
            for (int i = 0; i < _propertyRefs.Count; i++)
            {
                PropertyRef propertyRef = _propertyRefs[i];
                if (TryIsPropertyRefEqual(ref propertyRef, propertyName, key, ref info))
                {
                    break;
                }
            }

            if (!hasPropertyCache)
            {
                if (propertyIndex == 0 && frame.PropertyRefCache == null)
                {
                    // Create the temporary list on first property access to prevent a partially filled List.
                    frame.PropertyRefCache = new List <PropertyRef>();
                }

                if (info != null)
                {
                    Debug.Assert(frame.PropertyRefCache != null);
                    frame.PropertyRefCache.Add(new PropertyRef(key, info));
                }
            }

            return(info);
        }
Esempio n. 15
0
 public void Pop()
 {
     Debug.Assert(_index > 0);
     Current = _previous[--_index];
 }