private void CacheMember( Type declaringType, Type memberType, MemberInfo memberInfo, JsonNumberHandling?typeNumberHandling, Dictionary <string, JsonPropertyInfo> cache, ref Dictionary <string, MemberInfo>?ignoredMembers) { JsonPropertyInfo jsonPropertyInfo = AddProperty(memberInfo, memberType, declaringType, typeNumberHandling, Options); Debug.Assert(jsonPropertyInfo.NameAsString != null); string memberName = memberInfo.Name; // The JsonPropertyNameAttribute or naming policy resulted in a collision. if (!JsonHelpers.TryAdd(cache, jsonPropertyInfo.NameAsString, jsonPropertyInfo)) { JsonPropertyInfo other = cache[jsonPropertyInfo.NameAsString]; if (other.IsIgnored) { // Overwrite previously cached property since it has [JsonIgnore]. cache[jsonPropertyInfo.NameAsString] = jsonPropertyInfo; } else if ( // Does the current property have `JsonIgnoreAttribute`? !jsonPropertyInfo.IsIgnored && // Is the current property hidden by the previously cached property // (with `new` keyword, or by overriding)? other.MemberInfo !.Name != memberName && // Was a property with the same CLR name was ignored? That property hid the current property, // thus, if it was ignored, the current property should be ignored too. ignoredMembers?.ContainsKey(memberName) != true) { // We throw if we have two public properties that have the same JSON property name, and neither have been ignored. ThrowHelper.ThrowInvalidOperationException_SerializerPropertyNameConflict(Type, jsonPropertyInfo); } // Ignore the current property. } if (jsonPropertyInfo.IsIgnored) { (ignoredMembers ??= new Dictionary <string, MemberInfo>()).Add(memberName, memberInfo); } }
public 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]. } } } if (DetermineExtensionDataProperty(cache)) { // Remove from cache since it is handled independently. cache.Remove(DataExtensionProperty.NameAsString); } // Set as a unit to avoid concurrency issues. PropertyCache = cache; } 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); Type objectType; if (IsNativelySupportedCollection(type)) { // Use the type from the property policy to get any late-bound concrete types (from an interface like IDictionary). objectType = PolicyProperty.RuntimePropertyType; } else { // We need to create the declared instance for types implementing natively supported collections. objectType = PolicyProperty.DeclaredPropertyType; } CreateObject = options.MemberAccessorStrategy.CreateConstructor(objectType); ElementType = GetElementType(type, parentType: null, memberInfo: null, options: options); } break; case ClassType.IDictionaryConstructible: { // Add a single property that maps to the class type so we can have policies applied. AddPolicyProperty(type, options); ElementType = GetElementType(type, parentType: null, memberInfo: null, options: options); CreateConcreteDictionary = options.MemberAccessorStrategy.CreateConstructor( typeof(Dictionary <,>).MakeGenericType(typeof(string), ElementType)); CreateObject = options.MemberAccessorStrategy.CreateConstructor(PolicyProperty.DeclaredPropertyType); } 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; } }
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); Dictionary <string, JsonPropertyInfo> cache = new Dictionary <string, JsonPropertyInfo>( Options.PropertyNameCaseInsensitive ? StringComparer.OrdinalIgnoreCase : StringComparer.Ordinal); HashSet <string>?ignoredProperties = null; // We start from the most derived type and ascend to the base type. for (Type?currentType = type; currentType != null; currentType = currentType.BaseType) { foreach (PropertyInfo propertyInfo in currentType.GetProperties(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.DeclaredOnly)) { // Ignore indexers if (propertyInfo.GetIndexParameters().Length > 0) { continue; } // For now we only support public properties (i.e. setter and/or getter is public). if (propertyInfo.GetMethod?.IsPublic == true || propertyInfo.SetMethod?.IsPublic == true) { JsonPropertyInfo jsonPropertyInfo = AddProperty(propertyInfo, currentType, options); Debug.Assert(jsonPropertyInfo != null && jsonPropertyInfo.NameAsString != null); string propertyName = propertyInfo.Name; // The JsonPropertyNameAttribute or naming policy resulted in a collision. if (!JsonHelpers.TryAdd(cache, jsonPropertyInfo.NameAsString, jsonPropertyInfo)) { JsonPropertyInfo other = cache[jsonPropertyInfo.NameAsString]; if (other.IsIgnored) { // Overwrite previously cached property since it has [JsonIgnore]. cache[jsonPropertyInfo.NameAsString] = jsonPropertyInfo; } else if ( // Does the current property have `JsonIgnoreAttribute`? !jsonPropertyInfo.IsIgnored && // Is the current property hidden by the previously cached property // (with `new` keyword, or by overriding)? other.PropertyInfo !.Name != propertyName && // Was a property with the same CLR name was ignored? That property hid the current property, // thus, if it was ignored, the current property should be ignored too. ignoredProperties?.Contains(propertyName) != true) { // We throw if we have two public properties that have the same JSON property name, and neither have been ignored. ThrowHelper.ThrowInvalidOperationException_SerializerPropertyNameConflict(Type, jsonPropertyInfo); } // Ignore the current property. } if (jsonPropertyInfo.IsIgnored) { (ignoredProperties ??= new HashSet <string>()).Add(propertyName); } } else { if (JsonPropertyInfo.GetAttribute <JsonIncludeAttribute>(propertyInfo) != null) { ThrowHelper.ThrowInvalidOperationException_JsonIncludeOnNonPublicInvalid(propertyInfo, currentType); } // Non-public properties should not be included for (de)serialization. } } } 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]; } // Copy the dictionary cache to the array cache. cache.Values.CopyTo(cacheArray, 0); // Set the array cache field at this point since it is completely initialized. // It can now be safely accessed by other threads. PropertyCacheArray = cacheArray; // Allow constructor parameter logic to remove items from the dictionary since the JSON // property values will be passed to the constructor and do not call a property setter. if (converter.ConstructorIsParameterized) { InitializeConstructorParameters(cache, converter.ConstructorInfo !); } // Set the dictionary cache field at this point since it is completely initialized. // It can now be safely accessed by other threads. PropertyCache = cache; } 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 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; PolicyProperty = CreatePolicyProperty(Type, runtimeType, converter, Options); switch (ClassType) { case ClassType.Object: { CreateObject = options.MemberAccessorStrategy.CreateConstructor(type); PropertyInfo[] properties = type.GetProperties(BindingFlags.Instance | BindingFlags.Public); 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 && 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(this, 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; } 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(); } }
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; } }
public JsonClassInfo(Type type, JsonSerializerOptions options) { Type = type; Options = options; ClassType = GetClassType( type, parentClassType: type, propertyInfo: null, out Type runtimeType, out Type elementType, out Type nullableUnderlyingType, out MethodInfo addMethod, out JsonConverter converter, checkForAddMethod: true, options); // Ignore properties on enumerable. 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; } // 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]. } } } 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; } break; case ClassType.Enumerable: case ClassType.Dictionary: { // Add a single property that maps to the class type so we can have policies applied. ElementType = elementType; AddItemToObject = addMethod; // A policy property is not a real property on a type; instead it leverages the existing converter // logic and generic support to avoid boxing. It is used with values types, elements from collections and // dictionaries, and collections themselves. Typically it would represent a CLR type such as System.String. PolicyProperty = CreateProperty( declaredPropertyType: type, runtimePropertyType: runtimeType, propertyInfo: null, // Not a real property so this is null. parentClassType: typeof(object), collectionElementType: elementType, nullableUnderlyingType, converter: null, ClassType, options); CreateObject = options.MemberAccessorStrategy.CreateConstructor(PolicyProperty.RuntimePropertyType); } break; case ClassType.Value: { CreateObject = options.MemberAccessorStrategy.CreateConstructor(type); // Add a single property that maps to the class type so we can have policies applied. //AddPolicyPropertyForValue(type, options); PolicyProperty = CreateProperty( declaredPropertyType: type, runtimePropertyType: runtimeType, propertyInfo: null, // Not a real property so this is null. parentClassType: typeof(object), collectionElementType: null, nullableUnderlyingType, converter, ClassType, options); } break; case ClassType.Unknown: { CreateObject = options.MemberAccessorStrategy.CreateConstructor(type); // Add a single property that maps to the class type so we can have policies applied. //AddPolicyPropertyForValue(type, options); PolicyProperty = CreateProperty( declaredPropertyType: type, runtimePropertyType: runtimeType, propertyInfo: null, // Not a real property so this is null. parentClassType: typeof(object), collectionElementType: null, nullableUnderlyingType, converter, ClassType, options); PropertyCache = new Dictionary <string, JsonPropertyInfo>(); PropertyCacheArray = Array.Empty <JsonPropertyInfo>(); } break; default: Debug.Fail($"Unexpected class type: {ClassType}"); break; } }
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); Dictionary <string, JsonPropertyInfo> cache = new Dictionary <string, JsonPropertyInfo>( Options.PropertyNameCaseInsensitive ? StringComparer.OrdinalIgnoreCase : StringComparer.Ordinal); Dictionary <string, PropertyInfo>?ignoredProperties = null; // We start from the most derived type. for (Type?currentType = type; currentType != null; currentType = currentType.BaseType) { foreach (PropertyInfo propertyInfo in currentType.GetProperties(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.DeclaredOnly)) { // Ignore indexers and virtual properties that have overrides that were [JsonIgnore]d. if (propertyInfo.GetIndexParameters().Length > 0 || PropertyIsOverridenAndIgnored(propertyInfo, ignoredProperties)) { continue; } // For now we only support public properties (i.e. setter and/or getter is public). if (propertyInfo.GetMethod?.IsPublic == true || propertyInfo.SetMethod?.IsPublic == true) { JsonPropertyInfo jsonPropertyInfo = AddProperty(propertyInfo, currentType, options); Debug.Assert(jsonPropertyInfo != null && jsonPropertyInfo.NameAsString != null); string propertyName = propertyInfo.Name; // The JsonPropertyNameAttribute or naming policy resulted in a collision. if (!JsonHelpers.TryAdd(cache, jsonPropertyInfo.NameAsString, jsonPropertyInfo)) { JsonPropertyInfo other = cache[jsonPropertyInfo.NameAsString]; if (other.IsIgnored) { // Overwrite previously cached property since it has [JsonIgnore]. cache[jsonPropertyInfo.NameAsString] = jsonPropertyInfo; } else if ( // Does the current property have `JsonIgnoreAttribute`? !jsonPropertyInfo.IsIgnored && // Is the current property hidden by the previously cached property // (with `new` keyword, or by overriding)? other.PropertyInfo !.Name != propertyName && // Was a property with the same CLR name ignored? That property hid the current property, // thus, if it was ignored, the current property should be ignored too. ignoredProperties?.ContainsKey(propertyName) != true ) { // Throw if we have two public properties with the same JSON property name, // neither overrides or hides the other, and neither have been ignored. ThrowHelper.ThrowInvalidOperationException_SerializerPropertyNameConflict(Type, jsonPropertyInfo); } // Ignore the current property. } if (jsonPropertyInfo.IsIgnored) { (ignoredProperties ??= new Dictionary <string, PropertyInfo>())[propertyName] = propertyInfo;
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); Dictionary <string, JsonPropertyInfo> cache = new Dictionary <string, JsonPropertyInfo>( Options.PropertyNameCaseInsensitive ? StringComparer.OrdinalIgnoreCase : StringComparer.Ordinal); for (Type?currentType = type; currentType != null; currentType = currentType.BaseType) { foreach (PropertyInfo propertyInfo in currentType.GetProperties(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.DeclaredOnly)) { // Ignore indexers if (propertyInfo.GetIndexParameters().Length > 0) { continue; } if (IsNonPublicProperty(propertyInfo)) { if (JsonPropertyInfo.GetAttribute <JsonIncludeAttribute>(propertyInfo) != null) { ThrowHelper.ThrowInvalidOperationException_JsonIncludeOnNonPublicInvalid(propertyInfo, currentType); } // 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, currentType, 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 (other.PropertyInfo?.Name != jsonPropertyInfo.PropertyInfo?.Name && (jsonPropertyInfo.ShouldDeserialize == true || jsonPropertyInfo.ShouldSerialize == true)) { // Check for name equality is required to determine when a new slot is used for the member. // Therefore, if names are not the same, there is conflict due to the name policy or attributes. ThrowHelper.ThrowInvalidOperationException_SerializerPropertyNameConflict(Type, jsonPropertyInfo); } // else ignore jsonPropertyInfo since it has [JsonIgnore] or it's hidden by a new slot. } } } } 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(); } }
internal JsonClassInfo(Type type, JsonSerializerOptions options) { Type = type; ClassType = GetClassType(type); CreateObject = options.ClassMaterializerStrategy.CreateConstructor(type); // Ignore properties on enumerable. switch (ClassType) { case ClassType.Object: { var propertyNames = new HashSet <string>(StringComparer.Ordinal); foreach (PropertyInfo propertyInfo in type.GetProperties(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic)) { // Ignore indexers if (propertyInfo.GetIndexParameters().Length > 0) { continue; } // For now we only support public getters\setters if (propertyInfo.GetMethod?.IsPublic == true || propertyInfo.SetMethod?.IsPublic == true) { JsonPropertyInfo jsonPropertyInfo = AddProperty(propertyInfo.PropertyType, propertyInfo, type, options); Debug.Assert(jsonPropertyInfo.NameUsedToCompareAsString != null); // If the JsonPropertyNameAttribute or naming policy results in collisions, throw an exception. if (!propertyNames.Add(jsonPropertyInfo.NameUsedToCompareAsString)) { ThrowHelper.ThrowInvalidOperationException_SerializerPropertyNameConflict(this, jsonPropertyInfo); } jsonPropertyInfo.ClearUnusedValuesAfterAdd(); } } DetermineExtensionDataProperty(); } break; case ClassType.Enumerable: case ClassType.Dictionary: { // Add a single property that maps to the class type so we can have policies applied. JsonPropertyInfo policyProperty = AddPolicyProperty(type, options); // Use the type from the property policy to get any late-bound concrete types (from an interface like IDictionary). CreateObject = options.ClassMaterializerStrategy.CreateConstructor(policyProperty.RuntimePropertyType); // Create a ClassInfo that maps to the element type which is used for (de)serialization and policies. Type elementType = GetElementType(type, parentType: null, memberInfo: null); ElementClassInfo = options.GetOrAddClass(elementType); } break; case ClassType.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); 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; // TODO: Utilize converter mechanism to handle (de)serialization of KeyValuePair // when it goes through: https://github.com/dotnet/corefx/issues/36639. case ClassType.KeyValuePair: { // For deserialization, we treat it as ClassType.IDictionaryConstructible so we can parse it like a dictionary // before using converter-like logic to create a KeyValuePair instance. // 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); // Make this Dictionary<string, object> to accomodate input of form {"Key": "MyKey", "Value": 1}. CreateObject = options.ClassMaterializerStrategy.CreateConstructor(typeof(Dictionary <string, object>)); // Create a ClassInfo that maps to the element type which is used for deserialization and policies. ElementClassInfo = options.GetOrAddClass(elementType); // For serialization, we treat it like ClassType.Object to utilize the public getters. // Add Key property PropertyInfo propertyInfo = type.GetProperty("Key", BindingFlags.Public | BindingFlags.Instance); JsonPropertyInfo jsonPropertyInfo = AddProperty(propertyInfo.PropertyType, propertyInfo, type, options); Debug.Assert(jsonPropertyInfo.NameUsedToCompareAsString != null); jsonPropertyInfo.ClearUnusedValuesAfterAdd(); // Add Value property. propertyInfo = type.GetProperty("Value", BindingFlags.Public | BindingFlags.Instance); jsonPropertyInfo = AddProperty(propertyInfo.PropertyType, propertyInfo, type, options); Debug.Assert(jsonPropertyInfo.NameUsedToCompareAsString != null); jsonPropertyInfo.ClearUnusedValuesAfterAdd(); } 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; } }