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); }
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; } }
// 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); } }
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); }
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++; }
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); }
// 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); } }
// 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); } } } }
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); }
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); }
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); }
public void Pop() { Debug.Assert(_index > 0); Current = _previous[--_index]; }