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; } }
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; } }