public static void ThrowInvalidOperationException_IgnoreConditionOnValueTypeInvalid(JsonPropertyInfo jsonPropertyInfo) { MemberInfo memberInfo = jsonPropertyInfo.MemberInfo !; throw new InvalidOperationException(SR.Format(SR.IgnoreConditionOnValueTypeInvalid, memberInfo.Name, memberInfo.DeclaringType)); }
public static void ThrowInvalidOperationException_NumberHandlingOnPropertyInvalid(JsonPropertyInfo jsonPropertyInfo) { MemberInfo?memberInfo = jsonPropertyInfo.MemberInfo; Debug.Assert(memberInfo != null); Debug.Assert(!jsonPropertyInfo.IsForTypeInfo); throw new InvalidOperationException(SR.Format(SR.NumberHandlingOnPropertyInvalid, memberInfo.Name, memberInfo.DeclaringType)); }
public static void ThrowInvalidOperationException_SerializerPropertyNameConflict(Type type, JsonPropertyInfo jsonPropertyInfo) { throw new InvalidOperationException(SR.Format(SR.SerializerPropertyNameConflict, type, jsonPropertyInfo.PropertyInfo?.Name)); }
// Copy any settings defined at run-time to the new property. public void CopyRuntimeSettingsTo(JsonPropertyInfo other) { other.EscapedName = EscapedName; other.Name = Name; other.PropertyNameKey = PropertyNameKey; }
private static void GetRuntimePropertyInfo(object?value, JsonClassInfo jsonClassInfo, ref JsonPropertyInfo jsonPropertyInfo, JsonSerializerOptions options) { if (value != null) { Type runtimeType = value.GetType(); // Nothing to do for typeof(object) if (runtimeType != typeof(object)) { jsonPropertyInfo = jsonClassInfo.GetOrAddPolymorphicProperty(jsonPropertyInfo, runtimeType, options); } } }
internal static JsonPropertyInfo CreateProperty( Type declaredPropertyType, Type runtimePropertyType, PropertyInfo propertyInfo, Type parentClassType, Type collectionElementType, Type nullableUnderlyingType, JsonConverter converter, ClassType classType, JsonSerializerOptions options) { bool treatAsNullable = nullableUnderlyingType != null; // Obtain the type of the JsonPropertyInfo class to construct. Type propertyInfoClassType; if (treatAsNullable && converter != null) { propertyInfoClassType = typeof(JsonPropertyInfoNullable <,>).MakeGenericType(parentClassType, nullableUnderlyingType); } else { Type typeToConvert = converter?.TypeToConvert; if (typeToConvert == null) { typeToConvert = declaredPropertyType; } // For the covariant case, create JsonPropertyInfoNotNullable. The generic constraints are "where TConverter : TDeclaredProperty". if (runtimePropertyType.IsAssignableFrom(typeToConvert)) { propertyInfoClassType = typeof(JsonPropertyInfoNotNullable <, , ,>).MakeGenericType( parentClassType, declaredPropertyType, runtimePropertyType, typeToConvert); } else { Debug.Assert(typeToConvert.IsAssignableFrom(runtimePropertyType)); // For the contravariant case, create JsonPropertyInfoNotNullableContravariant. The generic constraints are "where TDeclaredProperty : TConverter". propertyInfoClassType = typeof(JsonPropertyInfoNotNullableContravariant <, , ,>).MakeGenericType( parentClassType, declaredPropertyType, runtimePropertyType, typeToConvert); } } // Create the JsonPropertyInfo instance. JsonPropertyInfo jsonPropertyInfo = (JsonPropertyInfo)Activator.CreateInstance( propertyInfoClassType, BindingFlags.Instance | BindingFlags.Public, binder: null, args: null, culture: null); jsonPropertyInfo.Initialize( parentClassType, declaredPropertyType, runtimePropertyType, runtimeClassType: classType, propertyInfo, collectionElementType, converter, treatAsNullable, options); return(jsonPropertyInfo); }
internal JsonClassInfo(Type type, JsonSerializerOptions options) { Type = type; Options = options; ClassType = GetClassType(type, options); CreateObject = options.MemberAccessorStrategy.CreateConstructor(type); // Ignore properties on enumerable. switch (ClassType) { case ClassType.Object: { PropertyInfo[] properties = type.GetProperties(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); Dictionary <string, JsonPropertyInfo> cache = CreatePropertyCache(properties.Length); foreach (PropertyInfo propertyInfo in properties) { // 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 != null); // If the JsonPropertyNameAttribute or naming policy results in collisions, throw an exception. if (!JsonHelpers.TryAdd(cache, jsonPropertyInfo.NameAsString, jsonPropertyInfo)) { JsonPropertyInfo other = cache[jsonPropertyInfo.NameAsString]; if (other.ShouldDeserialize == false && other.ShouldSerialize == false) { // Overwrite the one just added since it has [JsonIgnore]. cache[jsonPropertyInfo.NameAsString] = jsonPropertyInfo; } else if (jsonPropertyInfo.ShouldDeserialize == true || jsonPropertyInfo.ShouldSerialize == true) { ThrowHelper.ThrowInvalidOperationException_SerializerPropertyNameConflict(this, jsonPropertyInfo); } // else ignore jsonPropertyInfo since it has [JsonIgnore]. } } } // Set as a unit to avoid concurrency issues. PropertyCache = cache; DetermineExtensionDataProperty(); } break; case ClassType.Enumerable: case ClassType.Dictionary: { // Add a single property that maps to the class type so we can have policies applied. AddPolicyProperty(type, options); // Use the type from the property policy to get any late-bound concrete types (from an interface like IDictionary). CreateObject = options.MemberAccessorStrategy.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, options: options); ElementClassInfo = options.GetOrAddClass(elementType); } break; case ClassType.IDictionaryConstructible: { // 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, options: options); CreateObject = options.MemberAccessorStrategy.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: // Add a single property that maps to the class type so we can have policies applied. AddPolicyProperty(type, options); break; case ClassType.Unknown: // Add a single property that maps to the class type so we can have policies applied. AddPolicyProperty(type, options); PropertyCache = new Dictionary <string, JsonPropertyInfo>(); break; default: Debug.Fail($"Unexpected class type: {ClassType}"); break; } }
private static bool HandleNull(JsonSerializerOptions options, ref Utf8JsonReader reader, ref ReadStack state) { if (state.Current.SkipProperty) { // Clear the current property in case it is a dictionary, since dictionaries must have EndProperty() called when completed. // A non-dictionary property can also have EndProperty() called when completed, although it is redundant. state.Current.EndProperty(); return(false); } JsonPropertyInfo jsonPropertyInfo = state.Current.JsonPropertyInfo; if (jsonPropertyInfo == null || (reader.CurrentDepth == 0 && jsonPropertyInfo.CanBeNull)) { Debug.Assert(state.IsLastFrame); Debug.Assert(state.Current.ReturnValue == null); return(true); } Debug.Assert(jsonPropertyInfo != null); if (state.Current.IsProcessingCollectionObject()) { AddNullToCollection(jsonPropertyInfo, ref reader, ref state); return(false); } if (state.Current.IsProcessingCollectionProperty()) { if (state.Current.CollectionPropertyInitialized) { // Add the element. AddNullToCollection(jsonPropertyInfo, ref reader, ref state); } else { // Set the property to null. ApplyObjectToEnumerable(null, ref state, setPropertyDirectly: true); // Reset so that `Is*Property` no longer returns true state.Current.EndProperty(); } return(false); } if (!jsonPropertyInfo.CanBeNull) { // Allow a value type converter to return a null value representation, such as JsonElement. // Most likely this will throw JsonException. jsonPropertyInfo.Read(JsonTokenType.Null, ref state, ref reader); return(false); } if (state.Current.ReturnValue == null) { Debug.Assert(state.IsLastFrame); return(true); } if (!jsonPropertyInfo.IgnoreNullValues) { state.Current.JsonPropertyInfo.SetValueAsObject(state.Current.ReturnValue, value: null); } return(false); }
public abstract IEnumerable CreateDerivedEnumerableInstance(JsonPropertyInfo collectionPropertyInfo, IList sourceList, string jsonPath, JsonSerializerOptions options);
internal static JsonPropertyInfo CreateProperty(Type declaredPropertyType, Type runtimePropertyType, PropertyInfo propertyInfo, Type parentClassType, JsonSerializerOptions options) { bool hasIgnoreAttribute = (JsonPropertyInfo.GetAttribute <JsonIgnoreAttribute>(propertyInfo) != null); if (hasIgnoreAttribute) { return(JsonPropertyInfo.CreateIgnoredPropertyPlaceholder(propertyInfo, options)); } Type collectionElementType = null; switch (GetClassType(runtimePropertyType, options)) { case ClassType.Enumerable: case ClassType.Dictionary: case ClassType.IDictionaryConstructible: case ClassType.Unknown: collectionElementType = GetElementType(runtimePropertyType, parentClassType, propertyInfo, options); break; } JsonConverter converter; // Create the JsonPropertyInfo<TType, TProperty> Type propertyInfoClassType; if (runtimePropertyType.IsGenericType && runtimePropertyType.GetGenericTypeDefinition() == typeof(Nullable <>)) { // For Nullable, use the underlying type. Type underlyingPropertyType = Nullable.GetUnderlyingType(runtimePropertyType); propertyInfoClassType = typeof(JsonPropertyInfoNullable <,>).MakeGenericType(parentClassType, underlyingPropertyType); converter = options.DetermineConverterForProperty(parentClassType, underlyingPropertyType, propertyInfo); } else { converter = options.DetermineConverterForProperty(parentClassType, runtimePropertyType, propertyInfo); Type typeToConvert = converter?.TypeToConvert; if (typeToConvert == null) { typeToConvert = runtimePropertyType; } // For the covariant case, create JsonPropertyInfoNotNullable. The generic constraints are "where TConverter : TDeclaredProperty". if (runtimePropertyType.IsAssignableFrom(typeToConvert)) { propertyInfoClassType = typeof(JsonPropertyInfoNotNullable <, , ,>).MakeGenericType( parentClassType, declaredPropertyType, runtimePropertyType, typeToConvert); } else { Debug.Assert(typeToConvert.IsAssignableFrom(runtimePropertyType)); // For the contravariant case, create JsonPropertyInfoNotNullableContravariant. The generic constraints are "where TDeclaredProperty : TConverter". propertyInfoClassType = typeof(JsonPropertyInfoNotNullableContravariant <, , ,>).MakeGenericType( parentClassType, declaredPropertyType, runtimePropertyType, typeToConvert); } } JsonPropertyInfo jsonInfo = (JsonPropertyInfo)Activator.CreateInstance( propertyInfoClassType, BindingFlags.Instance | BindingFlags.Public, binder: null, args: null, culture: null); jsonInfo.Initialize(parentClassType, declaredPropertyType, runtimePropertyType, propertyInfo, collectionElementType, converter, options); return(jsonInfo); }
private static bool HandleObject( JsonPropertyInfo jsonPropertyInfo, JsonSerializerOptions options, Utf8JsonWriter writer, ref WriteStack state) { Debug.Assert( state.Current.JsonClassInfo.ClassType == ClassType.Object || state.Current.JsonClassInfo.ClassType == ClassType.Unknown); if (!jsonPropertyInfo.ShouldSerialize) { state.Current.MoveToNextProperty = true; return(true); } 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(ref state, writer); state.Current.MoveToNextProperty = true; 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.MoveToNextProperty = true; } 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.MoveToNextProperty = true; } return(endOfEnumerable); } // A property that returns a type that is deserialized by passing an // IDictionary to its constructor keeps the same stack frame. if (jsonPropertyInfo.ClassType == ClassType.IDictionaryConstructible) { state.Current.IsIDictionaryConstructibleProperty = true; bool endOfEnumerable = HandleDictionary(jsonPropertyInfo.ElementClassInfo, options, writer, ref state); if (endOfEnumerable) { state.Current.MoveToNextProperty = true; } 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.MoveToNextProperty = true; JsonClassInfo nextClassInfo = jsonPropertyInfo.RuntimeClassInfo; 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.Value); } state.Current.MoveToNextProperty = true; } return(true); }
internal JsonClassInfo(Type type, JsonSerializerOptions options) { Type = type; ClassType = GetClassType(type, options); 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, options: options); ElementClassInfo = options.GetOrAddClass(elementType); } break; case ClassType.IDictionaryConstructible: { // 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, options: options); 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; } }
private static bool TryIsPropertyRefEqual(ref PropertyRef propertyRef, ReadOnlySpan <byte> propertyName, ulong key, ref JsonPropertyInfo info) { if (key == propertyRef.Key) { if (propertyName.Length <= PropertyNameKeyLength || // We compare the whole name, although we could skip the first 6 bytes (but it's likely not any faster) propertyName.SequenceEqual(propertyRef.Info.NameUsedToCompare)) { info = propertyRef.Info; return(true); } } 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 HashSet <PropertyRef>(); } if (info != null) { Debug.Assert(frame.PropertyRefCache != null); frame.PropertyRefCache.Add(new PropertyRef(key, info)); } } return(info); }
// If this method is changed, also change ApplyValueToEnumerable. internal static void ApplyObjectToEnumerable( object value, ref ReadStack state, bool setPropertyDirectly = false) { Debug.Assert(!state.Current.SkipProperty); if (state.Current.IsProcessingObject(ClassType.Enumerable)) { if (state.Current.TempEnumerableValues != null) { state.Current.TempEnumerableValues.Add(value); } else { if (state.Current.AddObjectToEnumerable == null) { if (state.Current.ReturnValue is IList list) { list.Add(value); } else { ThrowHelper.ThrowJsonException_DeserializeUnableToConvertValue(value.GetType()); return; } } else { state.Current.JsonPropertyInfo.AddObjectToEnumerableWithReflection(state.Current.AddObjectToEnumerable, value); } } } else if (!setPropertyDirectly && state.Current.IsProcessingProperty(ClassType.Enumerable)) { Debug.Assert(state.Current.JsonPropertyInfo != null); Debug.Assert(state.Current.ReturnValue != null); if (state.Current.TempEnumerableValues != null) { state.Current.TempEnumerableValues.Add(value); } else { JsonPropertyInfo jsonPropertyInfo = state.Current.JsonPropertyInfo; object currentEnumerable = jsonPropertyInfo.GetValueAsObject(state.Current.ReturnValue); if (currentEnumerable == null || // ImmutableArray<T> is a struct, so default value won't be null. jsonPropertyInfo.IsImmutableArray) { jsonPropertyInfo.SetValueAsObject(state.Current.ReturnValue, value); } else if (state.Current.AddObjectToEnumerable == null) { ((IList)currentEnumerable).Add(value); } else { jsonPropertyInfo.AddObjectToEnumerableWithReflection(state.Current.AddObjectToEnumerable, value); } } } else if (state.Current.IsProcessingObject(ClassType.Dictionary) || (state.Current.IsProcessingProperty(ClassType.Dictionary) && !setPropertyDirectly)) { string key = state.Current.KeyName; Debug.Assert(!string.IsNullOrEmpty(key)); if (state.Current.TempDictionaryValues != null) { (state.Current.TempDictionaryValues)[key] = value; } else { Debug.Assert(state.Current.ReturnValue != null); object currentDictionary = state.Current.JsonPropertyInfo.GetValueAsObject(state.Current.ReturnValue); if (currentDictionary is IDictionary dict) { Debug.Assert(!dict.IsReadOnly); dict[key] = value; } else { state.Current.JsonPropertyInfo.AddObjectToDictionary(currentDictionary, key, value); } } } else { Debug.Assert(state.Current.JsonPropertyInfo != null); state.Current.JsonPropertyInfo.SetValueAsObject(state.Current.ReturnValue, value); } }
public abstract object CreateDerivedDictionaryInstance(JsonPropertyInfo collectionPropertyInfo, IDictionary sourceDictionary, string jsonPath, JsonSerializerOptions options);
// If this method is changed, also change ApplyObjectToEnumerable. internal static void ApplyValueToEnumerable <TProperty>( ref TProperty value, ref ReadStack state) { Debug.Assert(!state.Current.SkipProperty); if (state.Current.IsProcessingObject(ClassType.Enumerable)) { if (state.Current.TempEnumerableValues != null) { ((IList <TProperty>)state.Current.TempEnumerableValues).Add(value); } else { AddValueToEnumerable(ref state, state.Current.ReturnValue, value); } } else if (state.Current.IsProcessingProperty(ClassType.Enumerable)) { if (state.Current.TempEnumerableValues != null) { ((IList <TProperty>)state.Current.TempEnumerableValues).Add(value); } else { Debug.Assert(state.Current.JsonPropertyInfo != null); Debug.Assert(state.Current.ReturnValue != null); JsonPropertyInfo jsonPropertyInfo = state.Current.JsonPropertyInfo; object currentEnumerable = jsonPropertyInfo.GetValueAsObject(state.Current.ReturnValue); if (currentEnumerable == null || // ImmutableArray<T> is a struct, so default value won't be null. jsonPropertyInfo.IsImmutableArray) { jsonPropertyInfo.SetValueAsObject(state.Current.ReturnValue, value); } else { AddValueToEnumerable(ref state, currentEnumerable, value); } } } else if (state.Current.IsProcessingDictionary()) { string key = state.Current.KeyName; Debug.Assert(!string.IsNullOrEmpty(key)); if (state.Current.TempDictionaryValues != null) { ((IDictionary <string, TProperty>)state.Current.TempDictionaryValues)[key] = value; } else { Debug.Assert(state.Current.ReturnValue != null); object currentDictionary = state.Current.JsonPropertyInfo.GetValueAsObject(state.Current.ReturnValue); if (currentDictionary is IDictionary <string, TProperty> genericDict) { Debug.Assert(!genericDict.IsReadOnly); genericDict[key] = value; } else if (currentDictionary is IDictionary dict) { Debug.Assert(!dict.IsReadOnly); dict[key] = value; } else { throw ThrowHelper.GetNotSupportedException_SerializationNotSupportedCollection(currentDictionary.GetType(), parentType: null, memberInfo: null); } } } else { Debug.Assert(state.Current.JsonPropertyInfo != null); state.Current.JsonPropertyInfo.SetValueAsObject(state.Current.ReturnValue, value); } }
internal JsonPropertyInfo GetOrAddPolymorphicProperty(JsonPropertyInfo property, Type runtimePropertyType, JsonSerializerOptions options) {
internal JsonPropertyInfo GetProperty(ReadOnlySpan <byte> propertyName, ref ReadStackFrame frame) { JsonPropertyInfo info = null; // If we're not trying to build the cache locally, and there is an existing cache, then use it. if (_propertyRefsSorted != null) { ulong key = GetKey(propertyName); // First try sorted lookup. int propertyIndex = frame.PropertyIndex; // This .Length is consistent no matter what json data intialized _propertyRefsSorted. int count = _propertyRefsSorted.Length; if (count != 0) { int iForward = Math.Min(propertyIndex, count); int iBackward = iForward - 1; while (iForward < count || iBackward >= 0) { if (iForward < count) { if (TryIsPropertyRefEqual(_propertyRefsSorted[iForward], propertyName, key, ref info)) { return(info); } ++iForward; } if (iBackward >= 0) { if (TryIsPropertyRefEqual(_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. string stringPropertyName = JsonHelpers.Utf8GetString(propertyName); if (PropertyCache.TryGetValue(stringPropertyName, out info)) { // For performance, only add to cache up to a threshold and then just use the dictionary. int count; if (_propertyRefsSorted != null) { count = _propertyRefsSorted.Length; } else { count = 0; } // Do a quick check for the stable (after warm-up) case. if (count < PropertyNameCountCacheThreshold) { if (frame.PropertyRefCache != null) { count += frame.PropertyRefCache.Count; } // Check again to fill up to the limit. if (count < PropertyNameCountCacheThreshold) { if (frame.PropertyRefCache == null) { frame.PropertyRefCache = new List <PropertyRef>(); } ulong key = info.PropertyNameKey; PropertyRef propertyRef = new PropertyRef(key, info); frame.PropertyRefCache.Add(propertyRef); } } } return(info); }
public static object CreateEnumerableValue(ref Utf8JsonReader reader, ref ReadStack state) { 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.PolicyProperty.CreateConverterList(); } else { converterList = new List <object>(); } state.Current.TempEnumerableValues = converterList; // Clear the value if present to ensure we don't confuse tempEnumerableValues with the collection. if (!jsonPropertyInfo.IsPropertyPolicy && !state.Current.JsonPropertyInfo.RuntimePropertyType.FullName.StartsWith(DefaultImmutableEnumerableConverter.ImmutableArrayGenericTypeName)) { jsonPropertyInfo.SetValueAsObject(state.Current.ReturnValue, null); } return(null); } Type propertyType = jsonPropertyInfo.RuntimePropertyType; if (typeof(IList).IsAssignableFrom(propertyType)) { // If IList, add the members as we create them. JsonClassInfo collectionClassInfo; if (jsonPropertyInfo.DeclaredPropertyType == jsonPropertyInfo.ImplementedPropertyType) { collectionClassInfo = jsonPropertyInfo.RuntimeClassInfo; } else { collectionClassInfo = jsonPropertyInfo.DeclaredTypeClassInfo; } if (collectionClassInfo.CreateObject() is IList collection) { return(collection); } else { ThrowHelper.ThrowJsonException_DeserializeUnableToConvertValue(jsonPropertyInfo.DeclaredPropertyType, reader, state.JsonPath); return(null); } } else { ThrowHelper.ThrowJsonException_DeserializeUnableToConvertValue(propertyType, reader, state.JsonPath); return(null); } }
private static bool HandleDictionary( JsonClassInfo elementClassInfo, JsonSerializerOptions options, Utf8JsonWriter writer, ref WriteStack state) { JsonPropertyInfo jsonPropertyInfo = state.Current.JsonPropertyInfo; if (state.Current.CollectionEnumerator == null) { IEnumerable enumerable; enumerable = (IEnumerable)jsonPropertyInfo.GetValueAsObject(state.Current.CurrentValue); if (enumerable == null) { if (!state.Current.JsonPropertyInfo.IgnoreNullValues) { // Write a null object or enumerable. state.Current.WriteObjectOrArrayStart(ClassType.Enumerable, writer, writeNull: true); } return(true); } if (enumerable is IDictionary dictionary) { state.Current.CollectionEnumerator = dictionary.GetEnumerator(); } else { state.Current.CollectionEnumerator = enumerable.GetEnumerator(); } if (state.Current.ExtensionDataStatus != ExtensionDataWriteStatus.Writing) { state.Current.WriteObjectOrArrayStart(ClassType.Dictionary, writer); } } if (state.Current.CollectionEnumerator.MoveNext()) { // Check for polymorphism. if (elementClassInfo.ClassType == ClassType.Unknown) { object currentValue = ((IDictionaryEnumerator)state.Current.CollectionEnumerator).Entry.Value; GetRuntimeClassInfo(currentValue, ref elementClassInfo, options); } if (elementClassInfo.ClassType == ClassType.Value) { elementClassInfo.PolicyProperty.WriteDictionary(ref state, writer); } else if (state.Current.CollectionEnumerator.Current == null) { writer.WriteNull(jsonPropertyInfo.Name); } else { // An object or another enumerator requires a new stack frame. var enumerator = (IDictionaryEnumerator)state.Current.CollectionEnumerator; object value = enumerator.Value; state.Push(elementClassInfo, value); state.Current.KeyName = (string)enumerator.Key; } return(false); } // We are done enumerating. if (state.Current.ExtensionDataStatus == ExtensionDataWriteStatus.Writing) { state.Current.ExtensionDataStatus = ExtensionDataWriteStatus.Finished; } else { writer.WriteEndObject(); } if (state.Current.PopStackOnEndCollection) { state.Pop(); } else { state.Current.EndDictionary(); } return(true); }
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.IsProcessingIDictionaryConstructible) { state.Current.TempDictionaryValues = (IDictionary)classInfo.CreateObject(); } else { if (classInfo.CreateObject == null) { ThrowHelper.ThrowJsonException_DeserializeUnableToConvertValue(classInfo.Type, reader, state.JsonPath); return; } state.Current.ReturnValue = classInfo.CreateObject(); } return; } state.Current.PropertyInitialized = true; if (state.Current.IsProcessingIDictionaryConstructible) { 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 void HandlePropertyName( JsonSerializerOptions options, ref Utf8JsonReader reader, ref ReadStack state) { if (state.Current.Drain) { return; } Debug.Assert(state.Current.ReturnValue != default || state.Current.TempDictionaryValues != default); Debug.Assert(state.Current.JsonClassInfo != default); if ((state.Current.IsProcessingDictionary || state.Current.IsProcessingIDictionaryConstructible) && state.Current.JsonClassInfo.DataExtensionProperty != state.Current.JsonPropertyInfo) { if (state.Current.IsDictionary || state.Current.IsIDictionaryConstructible) { state.Current.JsonPropertyInfo = state.Current.JsonClassInfo.PolicyProperty; } Debug.Assert( state.Current.IsDictionary || (state.Current.IsDictionaryProperty && state.Current.JsonPropertyInfo != null) || state.Current.IsIDictionaryConstructible || (state.Current.IsIDictionaryConstructibleProperty && state.Current.JsonPropertyInfo != null)); state.Current.KeyName = reader.GetString(); } else { state.Current.EndProperty(); ReadOnlySpan <byte> propertyName = reader.HasValueSequence ? reader.ValueSequence.ToArray() : reader.ValueSpan; if (reader._stringHasEscaping) { int idx = propertyName.IndexOf(JsonConstants.BackSlash); Debug.Assert(idx != -1); propertyName = GetUnescapedString(propertyName, idx); } JsonPropertyInfo jsonPropertyInfo = state.Current.JsonClassInfo.GetProperty(propertyName, ref state.Current); if (jsonPropertyInfo == JsonPropertyInfo.s_missingProperty) { JsonPropertyInfo dataExtProperty = state.Current.JsonClassInfo.DataExtensionProperty; if (dataExtProperty == null) { state.Current.JsonPropertyInfo = JsonPropertyInfo.s_missingProperty; } else { state.Current.JsonPropertyInfo = dataExtProperty; state.Current.JsonPropertyName = propertyName.ToArray(); state.Current.KeyName = JsonHelpers.Utf8GetString(propertyName); state.Current.CollectionPropertyInitialized = true; CreateDataExtensionProperty(dataExtProperty, ref state); } } else { // Support JsonException.Path. Debug.Assert( jsonPropertyInfo.JsonPropertyName == null || options.PropertyNameCaseInsensitive || propertyName.SequenceEqual(jsonPropertyInfo.JsonPropertyName)); state.Current.JsonPropertyInfo = jsonPropertyInfo; if (jsonPropertyInfo.JsonPropertyName == null) { byte[] propertyNameArray = propertyName.ToArray(); if (options.PropertyNameCaseInsensitive) { // Each payload can have a different name here; remember the value on the temporary stack. state.Current.JsonPropertyName = propertyNameArray; } else { // Prevent future allocs by caching globally on the JsonPropertyInfo which is specific to a Type+PropertyName // so it will match the incoming payload except when case insensitivity is enabled (which is handled above). state.Current.JsonPropertyInfo.JsonPropertyName = propertyNameArray; } } } // Increment the PropertyIndex so JsonClassInfo.GetProperty() starts with the next property. state.Current.PropertyIndex++; } }
public JsonClassInfo(Type type, JsonSerializerOptions options) { Type = type; Options = options; JsonConverter converter = GetConverter( Type, parentClassType: null, // A ClassInfo never has a "parent" class. propertyInfo: null, // A ClassInfo never has a "parent" property. out Type runtimeType, Options); ClassType = converter.ClassType; PropertyInfoForClassInfo = CreatePropertyInfoForClassInfo(Type, runtimeType, converter, Options); switch (ClassType) { case ClassType.Object: { CreateObject = options.MemberAccessorStrategy.CreateConstructor(type); PropertyInfo[] properties = type.GetProperties(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); Dictionary <string, JsonPropertyInfo> cache = CreatePropertyCache(properties.Length); foreach (PropertyInfo propertyInfo in properties) { // Ignore indexers if (propertyInfo.GetIndexParameters().Length > 0) { continue; } if (IsNonPublicProperty(propertyInfo)) { if (JsonPropertyInfo.GetAttribute <JsonIncludeAttribute>(propertyInfo) != null) { ThrowHelper.ThrowInvalidOperationException_JsonIncludeOnNonPublicInvalid(propertyInfo, Type); } // Non-public properties should not be included for (de)serialization. continue; } // For now we only support public getters\setters if (propertyInfo.GetMethod?.IsPublic == true || propertyInfo.SetMethod?.IsPublic == true) { JsonPropertyInfo jsonPropertyInfo = AddProperty(propertyInfo, type, options); Debug.Assert(jsonPropertyInfo != null && jsonPropertyInfo.NameAsString != null); // If the JsonPropertyNameAttribute or naming policy results in collisions, throw an exception. if (!JsonHelpers.TryAdd(cache, jsonPropertyInfo.NameAsString, jsonPropertyInfo)) { JsonPropertyInfo other = cache[jsonPropertyInfo.NameAsString]; if (other.ShouldDeserialize == false && other.ShouldSerialize == false) { // Overwrite the one just added since it has [JsonIgnore]. cache[jsonPropertyInfo.NameAsString] = jsonPropertyInfo; } else if (jsonPropertyInfo.ShouldDeserialize == true || jsonPropertyInfo.ShouldSerialize == true) { ThrowHelper.ThrowInvalidOperationException_SerializerPropertyNameConflict(Type, jsonPropertyInfo); } // else ignore jsonPropertyInfo since it has [JsonIgnore]. } } } JsonPropertyInfo[] cacheArray; if (DetermineExtensionDataProperty(cache)) { // Remove from cache since it is handled independently. cache.Remove(DataExtensionProperty !.NameAsString !); cacheArray = new JsonPropertyInfo[cache.Count + 1]; // Set the last element to the extension property. cacheArray[cache.Count] = DataExtensionProperty; } else { cacheArray = new JsonPropertyInfo[cache.Count]; } // Set fields when finished to avoid concurrency issues. PropertyCache = cache; cache.Values.CopyTo(cacheArray, 0); PropertyCacheArray = cacheArray; if (converter.ConstructorIsParameterized) { InitializeConstructorParameters(converter.ConstructorInfo !); } } break; case ClassType.Enumerable: case ClassType.Dictionary: { ElementType = converter.ElementType; CreateObject = options.MemberAccessorStrategy.CreateConstructor(runtimeType); } break; case ClassType.Value: case ClassType.NewValue: { CreateObject = options.MemberAccessorStrategy.CreateConstructor(type); } break; case ClassType.None: { ThrowHelper.ThrowNotSupportedException_SerializationNotSupported(type); } break; default: Debug.Fail($"Unexpected class type: {ClassType}"); throw new InvalidOperationException(); } }
public static void ThrowInvalidOperationException_ExtensionDataCannotBindToCtorParam(JsonPropertyInfo jsonPropertyInfo) { throw new InvalidOperationException(SR.Format(SR.ExtensionDataCannotBindToCtorParam, jsonPropertyInfo.ClrName, jsonPropertyInfo.DeclaringType)); }
public static void ThrowInvalidOperationException_NumberHandlingOnPropertyInvalid(JsonPropertyInfo jsonPropertyInfo) { MemberInfo?memberInfo = jsonPropertyInfo.MemberInfo; if (!jsonPropertyInfo.ConverterBase.IsInternalConverter) { throw new InvalidOperationException(SR.Format( SR.NumberHandlingConverterMustBeBuiltIn, jsonPropertyInfo.ConverterBase.GetType(), jsonPropertyInfo.IsForTypeInfo ? jsonPropertyInfo.DeclaredPropertyType : memberInfo !.DeclaringType)); } // This exception is only thrown for object properties. Debug.Assert(!jsonPropertyInfo.IsForTypeInfo && memberInfo != null); throw new InvalidOperationException(SR.Format( SR.NumberHandlingOnPropertyTypeMustBeNumberOrCollection, memberInfo.Name, memberInfo.DeclaringType)); }
public void EndProperty() { JsonPropertyInfo = null; KeyName = null; MoveToNextProperty = false; }
private static void HandleStartArray(JsonSerializerOptions options, ref ReadStack state) { if (state.Current.SkipProperty) { // The array is not being applied to the object. state.Push(); state.Current.Drain = true; return; } JsonPropertyInfo jsonPropertyInfo = state.Current.JsonPropertyInfo; if (jsonPropertyInfo == null) { jsonPropertyInfo = state.Current.JsonClassInfo.CreateRootProperty(options); } else if (state.Current.JsonClassInfo.ClassType == ClassType.Unknown) { jsonPropertyInfo = state.Current.JsonClassInfo.GetOrAddPolymorphicProperty(jsonPropertyInfo, typeof(object), options); } // Verify that we have a valid enumerable. Type arrayType = jsonPropertyInfo.RuntimePropertyType; if (!typeof(IEnumerable).IsAssignableFrom(arrayType)) { ThrowHelper.ThrowJsonException_DeserializeUnableToConvertValue(arrayType); } Debug.Assert(state.Current.IsProcessingCollection()); if (state.Current.CollectionPropertyInitialized) { // A nested json array so push a new stack frame. Type elementType = jsonPropertyInfo.ElementClassInfo.Type; state.Push(); state.Current.Initialize(elementType, options); } state.Current.CollectionPropertyInitialized = true; // We should not be processing custom converters here (converters are of ClassType.Value). Debug.Assert(state.Current.JsonClassInfo.ClassType != ClassType.Value); // Set or replace the existing enumerable value. object value = ReadStackFrame.CreateEnumerableValue(ref state); // If value is not null, then we don't have a converter so apply the value. if (value != null) { state.Current.DetermineEnumerablePopulationStrategy(value); if (state.Current.ReturnValue != null) { state.Current.JsonPropertyInfo.SetValueAsObject(state.Current.ReturnValue, value); } else { state.Current.ReturnValue = value; } } }
public static void ThrowInvalidOperationException_SerializerPropertyNameNull(Type parentType, JsonPropertyInfo jsonPropertyInfo) { throw new InvalidOperationException(SR.Format(SR.SerializerPropertyNameNull, parentType, jsonPropertyInfo.PropertyInfo?.Name)); }
private static void HandleStartArray( JsonSerializerOptions options, ref Utf8JsonReader reader, ref ReadStack state) { JsonPropertyInfo jsonPropertyInfo = state.Current.JsonPropertyInfo; if (state.Current.SkipProperty) { // 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); } // Verify that we don't have a multidimensional array. Type arrayType = jsonPropertyInfo.RuntimePropertyType; if (!state.Current.IsProcessingKeyValuePair && !typeof(IEnumerable).IsAssignableFrom(arrayType) || (arrayType.IsArray && arrayType.GetArrayRank() > 1)) { ThrowHelper.ThrowJsonException_DeserializeUnableToConvertValue(arrayType, reader, state.JsonPath); } Debug.Assert(state.Current.IsProcessingEnumerableOrDictionary); if (state.Current.PropertyInitialized) { // A nested json array so push a new stack frame. Type elementType = jsonPropertyInfo.ElementClassInfo.Type; state.Push(); state.Current.Initialize(elementType, options); state.Current.PropertyInitialized = true; } else { state.Current.PropertyInitialized = 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 is not null, then we don't have a converter so apply the value. 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); } } } }