1 // Licensed to the .NET Foundation under one or more agreements.
2 // The .NET Foundation licenses this file to you under the MIT license.
3 // See the LICENSE file in the project root for more information.
5 using System.Collections;
6 using System.Collections.Generic;
7 using System.Diagnostics;
8 using System.Text.Json.Serialization.Converters;
10 namespace System.Text.Json
12 public static partial class JsonSerializer
14 private static void HandleStartArray(
15 JsonSerializerOptions options,
16 ref Utf8JsonReader reader,
19 if (state.Current.SkipProperty)
21 // The array is not being applied to the object.
23 state.Current.Drain = true;
27 JsonPropertyInfo jsonPropertyInfo = state.Current.JsonPropertyInfo;
28 if (jsonPropertyInfo == null)
30 jsonPropertyInfo = state.Current.JsonClassInfo.CreateRootObject(options);
32 else if (state.Current.JsonClassInfo.ClassType == ClassType.Unknown)
34 jsonPropertyInfo = state.Current.JsonClassInfo.CreatePolymorphicProperty(jsonPropertyInfo, typeof(object), options);
37 // Verify that we have a valid enumerable.
38 Type arrayType = jsonPropertyInfo.RuntimePropertyType;
39 if (!typeof(IEnumerable).IsAssignableFrom(arrayType))
41 ThrowHelper.ThrowJsonException_DeserializeUnableToConvertValue(arrayType);
44 Debug.Assert(state.Current.IsProcessingCollection());
46 if (state.Current.CollectionPropertyInitialized)
48 // A nested json array so push a new stack frame.
49 Type elementType = jsonPropertyInfo.ElementClassInfo.Type;
52 state.Current.Initialize(elementType, options);
55 state.Current.CollectionPropertyInitialized = true;
57 // We should not be processing custom converters here (converters are of ClassType.Value).
58 Debug.Assert(state.Current.JsonClassInfo.ClassType != ClassType.Value);
60 // Set or replace the existing enumerable value.
61 object value = ReadStackFrame.CreateEnumerableValue(ref reader, ref state);
63 // If value is not null, then we don't have a converter so apply the value.
66 if (state.Current.ReturnValue != null)
68 state.Current.JsonPropertyInfo.SetValueAsObject(state.Current.ReturnValue, value);
72 // Primitive arrays being returned without object
73 state.Current.SetReturnValue(value);
78 private static bool HandleEndArray(
79 JsonSerializerOptions options,
82 bool lastFrame = state.IsLastFrame;
84 if (state.Current.Drain)
86 // The array is not being applied to the object.
91 IEnumerable value = ReadStackFrame.GetEnumerableValue(ref state.Current);
93 if (state.Current.TempEnumerableValues != null)
95 // We have a converter; possibilities:
96 // - Add value to current frame's current property or TempEnumerableValues.
97 // - Add value to previous frame's current property or TempEnumerableValues.
98 // - Set current property on current frame to value.
99 // - Set current property on previous frame to value.
100 // - Set ReturnValue if root frame and value is the actual return value.
101 JsonEnumerableConverter converter = state.Current.JsonPropertyInfo.EnumerableConverter;
102 Debug.Assert(converter != null);
104 value = converter.CreateFromList(ref state, (IList)value, options);
105 state.Current.TempEnumerableValues = null;
107 else if (state.Current.IsProcessingProperty(ClassType.Enumerable))
109 // We added the items to the list already.
110 state.Current.EndProperty();
116 if (state.Current.ReturnValue == null)
118 // Returning a converted list or object.
119 state.Current.Reset();
120 state.Current.ReturnValue = value;
123 else if (state.Current.IsProcessingCollectionObject())
125 // Returning a non-converted list.
128 // else there must be an outer object, so we'll return false here.
130 else if (state.Current.IsProcessingObject(ClassType.Enumerable))
135 ApplyObjectToEnumerable(value, ref state);
140 // If this method is changed, also change ApplyValueToEnumerable.
141 internal static void ApplyObjectToEnumerable(
144 bool setPropertyDirectly = false)
146 Debug.Assert(!state.Current.SkipProperty);
148 if (state.Current.IsProcessingObject(ClassType.Enumerable))
150 if (state.Current.TempEnumerableValues != null)
152 state.Current.TempEnumerableValues.Add(value);
156 if (!(state.Current.ReturnValue is IList list))
158 ThrowHelper.ThrowJsonException_DeserializeUnableToConvertValue(value.GetType());
164 else if (!setPropertyDirectly && state.Current.IsProcessingProperty(ClassType.Enumerable))
166 Debug.Assert(state.Current.JsonPropertyInfo != null);
167 Debug.Assert(state.Current.ReturnValue != null);
168 if (state.Current.TempEnumerableValues != null)
170 state.Current.TempEnumerableValues.Add(value);
174 IList list = (IList)state.Current.JsonPropertyInfo.GetValueAsObject(state.Current.ReturnValue);
176 // ImmutableArray<T> is a struct, so default value won't be null.
177 state.Current.JsonPropertyInfo.RuntimePropertyType.FullName.StartsWith(DefaultImmutableEnumerableConverter.ImmutableArrayGenericTypeName))
179 state.Current.JsonPropertyInfo.SetValueAsObject(state.Current.ReturnValue, value);
187 else if (state.Current.IsProcessingObject(ClassType.Dictionary) ||
188 (state.Current.IsProcessingProperty(ClassType.Dictionary) && !setPropertyDirectly))
190 Debug.Assert(state.Current.ReturnValue != null);
191 IDictionary dictionary = (IDictionary)state.Current.JsonPropertyInfo.GetValueAsObject(state.Current.ReturnValue);
193 string key = state.Current.KeyName;
194 Debug.Assert(!string.IsNullOrEmpty(key));
195 dictionary[key] = value;
197 else if (state.Current.IsProcessingObject(ClassType.IDictionaryConstructible) ||
198 (state.Current.IsProcessingProperty(ClassType.IDictionaryConstructible) && !setPropertyDirectly))
200 Debug.Assert(state.Current.TempDictionaryValues != null);
201 IDictionary dictionary = (IDictionary)state.Current.TempDictionaryValues;
203 string key = state.Current.KeyName;
204 Debug.Assert(!string.IsNullOrEmpty(key));
205 dictionary[key] = value;
209 Debug.Assert(state.Current.JsonPropertyInfo != null);
210 state.Current.JsonPropertyInfo.SetValueAsObject(state.Current.ReturnValue, value);
214 // If this method is changed, also change ApplyObjectToEnumerable.
215 internal static void ApplyValueToEnumerable<TProperty>(
219 Debug.Assert(!state.Current.SkipProperty);
221 if (state.Current.IsProcessingObject(ClassType.Enumerable))
223 if (state.Current.TempEnumerableValues != null)
225 ((IList<TProperty>)state.Current.TempEnumerableValues).Add(value);
229 ((IList<TProperty>)state.Current.ReturnValue).Add(value);
232 else if (state.Current.IsProcessingProperty(ClassType.Enumerable))
234 Debug.Assert(state.Current.JsonPropertyInfo != null);
235 Debug.Assert(state.Current.ReturnValue != null);
236 if (state.Current.TempEnumerableValues != null)
238 ((IList<TProperty>)state.Current.TempEnumerableValues).Add(value);
242 IList<TProperty> list = (IList<TProperty>)state.Current.JsonPropertyInfo.GetValueAsObject(state.Current.ReturnValue);
245 state.Current.JsonPropertyInfo.SetValueAsObject(state.Current.ReturnValue, value);
253 else if (state.Current.IsProcessingDictionary())
255 Debug.Assert(state.Current.ReturnValue != null);
256 IDictionary<string, TProperty> dictionary = (IDictionary<string, TProperty>)state.Current.JsonPropertyInfo.GetValueAsObject(state.Current.ReturnValue);
258 string key = state.Current.KeyName;
259 Debug.Assert(!string.IsNullOrEmpty(key));
260 dictionary[key] = value;
262 else if (state.Current.IsProcessingIDictionaryConstructible())
264 Debug.Assert(state.Current.TempDictionaryValues != null);
265 IDictionary<string, TProperty> dictionary = (IDictionary<string, TProperty>)state.Current.TempDictionaryValues;
267 string key = state.Current.KeyName;
268 Debug.Assert(!string.IsNullOrEmpty(key));
269 dictionary[key] = value;
273 Debug.Assert(state.Current.JsonPropertyInfo != null);
274 state.Current.JsonPropertyInfo.SetValueAsObject(state.Current.ReturnValue, value);