Ejemplo n.º 1
0
        private void InitializeIfRequired()
        {
            if (_dictionary != null)
            {
                return;
            }

            bool caseInsensitive = Options.HasValue ? Options.Value.PropertyNameCaseInsensitive : false;
            var  dictionary      = new JsonPropertyDictionary <JsonNode>(caseInsensitive);

            if (_jsonElement.HasValue)
            {
                JsonElement jElement = _jsonElement.Value;

                foreach (JsonProperty jElementProperty in jElement.EnumerateObject())
                {
                    JsonNode?node = JsonNodeConverter.Create(jElementProperty.Value, Options);
                    if (node != null)
                    {
                        node.Parent = this;
                    }

                    dictionary.Add(new KeyValuePair <string, JsonNode?>(jElementProperty.Name, node));
                }

                _jsonElement = null;
            }

            _dictionary = dictionary;
        }
Ejemplo n.º 2
0
        internal void InitializeConstructorParameters(JsonParameterInfoValues[] jsonParameters, bool sourceGenMode = false)
        {
            var parameterCache = new JsonPropertyDictionary <JsonParameterInfo>(Options.PropertyNameCaseInsensitive, jsonParameters.Length);

            // Cache the lookup from object property name to JsonPropertyInfo using a case-insensitive comparer.
            // Case-insensitive is used to support both camel-cased parameter names and exact matches when C#
            // record types or anonymous types are used.
            // The property name key does not use [JsonPropertyName] or PropertyNamingPolicy since we only bind
            // the parameter name to the object property name and do not use the JSON version of the name here.
            var nameLookup = new Dictionary <ParameterLookupKey, ParameterLookupValue>(PropertyCache !.Count);

            foreach (KeyValuePair <string, JsonPropertyInfo?> kvp in PropertyCache.List)
            {
                JsonPropertyInfo jsonProperty = kvp.Value !;
                string           propertyName = jsonProperty.ClrName !;

                ParameterLookupKey   key   = new(propertyName, jsonProperty.PropertyType);
                ParameterLookupValue value = new(jsonProperty);

                if (!JsonHelpers.TryAdd(nameLookup, key, value))
                {
                    // More than one property has the same case-insensitive name and Type.
                    // Remember so we can throw a nice exception if this property is used as a parameter name.
                    ParameterLookupValue existing = nameLookup[key];
                    existing.DuplicateName = propertyName;
                }
            }

            foreach (JsonParameterInfoValues parameterInfo in jsonParameters)
            {
                ParameterLookupKey paramToCheck = new(parameterInfo.Name, parameterInfo.ParameterType);

                if (nameLookup.TryGetValue(paramToCheck, out ParameterLookupValue? matchingEntry))
                {
                    if (matchingEntry.DuplicateName != null)
                    {
                        // Multiple object properties cannot bind to the same constructor parameter.
                        ThrowHelper.ThrowInvalidOperationException_MultiplePropertiesBindToConstructorParameters(
                            Type,
                            parameterInfo.Name !,
                            matchingEntry.JsonPropertyInfo.Name,
                            matchingEntry.DuplicateName);
                    }

                    Debug.Assert(matchingEntry.JsonPropertyInfo != null);
                    JsonPropertyInfo  jsonPropertyInfo  = matchingEntry.JsonPropertyInfo;
                    JsonParameterInfo jsonParameterInfo = CreateConstructorParameter(parameterInfo, jsonPropertyInfo, sourceGenMode, Options);
                    parameterCache.Add(jsonPropertyInfo.Name, jsonParameterInfo);
                }
                // It is invalid for the extension data property to bind with a constructor argument.
                else if (DataExtensionProperty != null &&
                         StringComparer.OrdinalIgnoreCase.Equals(paramToCheck.Name, DataExtensionProperty.Name))
                {
                    ThrowHelper.ThrowInvalidOperationException_ExtensionDataCannotBindToCtorParam(DataExtensionProperty);
                }
            }

            ParameterCount = jsonParameters.Length;
            Volatile.Write(ref ParameterCache, parameterCache);
        }
Ejemplo n.º 3
0
        internal void CacheMember(JsonPropertyInfo jsonPropertyInfo, JsonPropertyDictionary <JsonPropertyInfo>?propertyCache, ref Dictionary <string, JsonPropertyInfo>?ignoredMembers)
        {
            string memberName = jsonPropertyInfo.ClrName !;

            // The JsonPropertyNameAttribute or naming policy resulted in a collision.
            if (!propertyCache !.TryAdd(jsonPropertyInfo.Name, jsonPropertyInfo))
            {
                JsonPropertyInfo other = propertyCache[jsonPropertyInfo.Name] !;

                if (other.IsIgnored)
                {
                    // Overwrite previously cached property since it has [JsonIgnore].
                    propertyCache[jsonPropertyInfo.Name] = 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.ClrName != 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, JsonPropertyInfo>()).Add(memberName, jsonPropertyInfo);
            }
        }
Ejemplo n.º 4
0
        private void AddPropertiesAndParametersUsingReflection()
        {
            Debug.Assert(PropertyInfoForTypeInfo.ConverterStrategy == ConverterStrategy.Object);

            const BindingFlags bindingFlags =
                BindingFlags.Instance |
                BindingFlags.Public |
                BindingFlags.NonPublic |
                BindingFlags.DeclaredOnly;

            Dictionary <string, JsonPropertyInfo>?ignoredMembers = null;

            PropertyInfo[] properties             = Type.GetProperties(bindingFlags);
            bool           propertyOrderSpecified = false;

            // PropertyCache is not accessed by other threads until the current JsonTypeInfo instance
            //  is finished initializing and added to the cache on JsonSerializerOptions.
            // Default 'capacity' to the common non-polymorphic + property case.
            PropertyCache = new JsonPropertyDictionary <JsonPropertyInfo>(Options.PropertyNameCaseInsensitive, capacity: properties.Length);

            // We start from the most derived type.
            Type?currentType = Type;

            while (true)
            {
                foreach (PropertyInfo propertyInfo in properties)
                {
                    bool   isVirtual    = propertyInfo.IsVirtual();
                    string propertyName = propertyInfo.Name;

                    // Ignore indexers and virtual properties that have overrides that were [JsonIgnore]d.
                    if (propertyInfo.GetIndexParameters().Length > 0 ||
                        PropertyIsOverridenAndIgnored(propertyName, propertyInfo.PropertyType, isVirtual, ignoredMembers))
                    {
                        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)
                    {
                        CacheMember(
                            currentType,
                            propertyInfo.PropertyType,
                            propertyInfo,
                            isVirtual,
                            NumberHandling,
                            ref propertyOrderSpecified,
                            ref ignoredMembers);
                    }
                    else
                    {
                        if (JsonPropertyInfo.GetAttribute <JsonIncludeAttribute>(propertyInfo) != null)
                        {
                            ThrowHelper.ThrowInvalidOperationException_JsonIncludeOnNonPublicInvalid(propertyName, currentType);
                        }

                        // Non-public properties should not be included for (de)serialization.
                    }
                }

                foreach (FieldInfo fieldInfo in currentType.GetFields(bindingFlags))
                {
                    string fieldName = fieldInfo.Name;

                    if (PropertyIsOverridenAndIgnored(fieldName, fieldInfo.FieldType, currentMemberIsVirtual: false, ignoredMembers))
                    {
                        continue;
                    }

                    bool hasJsonInclude = JsonPropertyInfo.GetAttribute <JsonIncludeAttribute>(fieldInfo) != null;

                    if (fieldInfo.IsPublic)
                    {
                        if (hasJsonInclude || Options.IncludeFields)
                        {
                            CacheMember(
                                currentType,
                                fieldInfo.FieldType,
                                fieldInfo,
                                isVirtual: false,
                                NumberHandling,
                                ref propertyOrderSpecified,
                                ref ignoredMembers);
                        }
                    }
                    else
                    {
                        if (hasJsonInclude)
                        {
                            ThrowHelper.ThrowInvalidOperationException_JsonIncludeOnNonPublicInvalid(fieldName, currentType);
                        }

                        // Non-public fields should not be included for (de)serialization.
                    }
                }

                currentType = currentType.BaseType;
                if (currentType == null)
                {
                    break;
                }

                properties = currentType.GetProperties(bindingFlags);
            }
            ;

            if (propertyOrderSpecified)
            {
                PropertyCache.List.Sort((p1, p2) => p1.Value !.Order.CompareTo(p2.Value !.Order));
            }
        }
Ejemplo n.º 5
0
        internal override void LateAddProperties()
        {
            Debug.Assert(!IsConfigured);
            Debug.Assert(PropertyCache is null);

            if (Kind != JsonTypeInfoKind.Object)
            {
                return;
            }

            JsonSerializerContext?context = Options.SerializerContext;

            JsonPropertyInfo[] array;
            if (PropInitFunc == null || (array = PropInitFunc(context !)) == null)
            {
                if (typeof(T) == typeof(object))
                {
                    return;
                }

                if (Converter.ElementType != null)
                {
                    // Nullable<> or F# optional converter's strategy is set to element's strategy
                    return;
                }

                if (SerializeHandler == null)
                {
                    ThrowHelper.ThrowInvalidOperationException_NoMetadataForTypeProperties(Options.TypeInfoResolver, Type);
                }

                MetadataSerializationNotSupported = true;
                return;
            }

            Dictionary <string, JsonPropertyInfo>?    ignoredMembers = null;
            JsonPropertyDictionary <JsonPropertyInfo> propertyCache  = CreatePropertyCache(capacity: array.Length);

            for (int i = 0; i < array.Length; i++)
            {
                JsonPropertyInfo jsonPropertyInfo = array[i];
                bool             hasJsonInclude   = jsonPropertyInfo.SrcGen_HasJsonInclude;

                if (!jsonPropertyInfo.SrcGen_IsPublic)
                {
                    if (hasJsonInclude)
                    {
                        Debug.Assert(jsonPropertyInfo.MemberName != null, "MemberName is not set by source gen");
                        ThrowHelper.ThrowInvalidOperationException_JsonIncludeOnNonPublicInvalid(jsonPropertyInfo.MemberName, jsonPropertyInfo.DeclaringType);
                    }

                    continue;
                }

                if (jsonPropertyInfo.MemberType == MemberTypes.Field && !hasJsonInclude && !Options.IncludeFields)
                {
                    continue;
                }

                CacheMember(jsonPropertyInfo, propertyCache, ref ignoredMembers);
            }

            PropertyCache = propertyCache;
        }
Ejemplo n.º 6
0
        internal void AddPropertiesUsingSourceGenInfo()
        {
            if (PropertyInfoForTypeInfo.ConverterStrategy != ConverterStrategy.Object)
            {
                return;
            }

            JsonSerializerContext?context = Options.SerializerContext;

            JsonPropertyInfo[] array;
            if (PropInitFunc == null || (array = PropInitFunc(context !)) == null)
            {
                if (typeof(T) == typeof(object))
                {
                    return;
                }

                if (Converter.ElementType != null)
                {
                    // Nullable<> or F# optional converter's strategy is set to element's strategy
                    return;
                }

                if (SerializeHandler != null && Options.SerializerContext?.CanUseSerializationLogic == true)
                {
                    ThrowOnDeserialize = true;
                    return;
                }

                ThrowHelper.ThrowInvalidOperationException_NoMetadataForTypeProperties(context, Type);
                return;
            }

            Dictionary <string, JsonPropertyInfo>?    ignoredMembers = null;
            JsonPropertyDictionary <JsonPropertyInfo> propertyCache  = CreatePropertyCache(capacity: array.Length);

            for (int i = 0; i < array.Length; i++)
            {
                JsonPropertyInfo jsonPropertyInfo = array[i];
                bool             hasJsonInclude   = jsonPropertyInfo.SrcGen_HasJsonInclude;

                if (!jsonPropertyInfo.SrcGen_IsPublic)
                {
                    if (hasJsonInclude)
                    {
                        Debug.Assert(jsonPropertyInfo.ClrName != null, "ClrName is not set by source gen");
                        ThrowHelper.ThrowInvalidOperationException_JsonIncludeOnNonPublicInvalid(jsonPropertyInfo.ClrName, jsonPropertyInfo.DeclaringType);
                    }

                    continue;
                }

                if (jsonPropertyInfo.MemberType == MemberTypes.Field && !hasJsonInclude && !Options.IncludeFields)
                {
                    continue;
                }

                if (jsonPropertyInfo.SrcGen_IsExtensionData)
                {
                    if (ExtensionDataProperty != null)
                    {
                        ThrowHelper.ThrowInvalidOperationException_SerializationDuplicateTypeAttribute(Type, typeof(JsonExtensionDataAttribute));
                    }

                    ExtensionDataProperty = jsonPropertyInfo;
                    continue;
                }

                CacheMember(jsonPropertyInfo, propertyCache, ref ignoredMembers);
            }

            PropertyCache = propertyCache;
        }