Exemplo n.º 1
0
        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);
            }
        }
Exemplo n.º 2
0
        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;
            }
        }
Exemplo n.º 3
0
        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();
            }
        }
Exemplo n.º 4
0
        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();
            }
        }
Exemplo n.º 5
0
        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;
            }
        }
Exemplo n.º 6
0
        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;
            }
        }
Exemplo n.º 7
0
        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;
Exemplo n.º 8
0
        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();
            }
        }
Exemplo n.º 9
0
        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;
            }
        }