Ejemplo n.º 1
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.º 2
0
        // Create a parameter that is ignored at run-time. It uses the same type (typeof(sbyte)) to help
        // prevent issues with unsupported types and helps ensure we don't accidently (de)serialize it.
        public static JsonParameterInfo CreateIgnoredParameterPlaceholder(JsonParameterInfoValues parameterInfo, JsonPropertyInfo matchingProperty)
        {
            JsonParameterInfo jsonParameterInfo = matchingProperty.ConverterBase.CreateJsonParameterInfo();

            jsonParameterInfo.ClrInfo             = parameterInfo;
            jsonParameterInfo.RuntimePropertyType = matchingProperty.RuntimePropertyType !;
            jsonParameterInfo.NameAsUtf8Bytes     = matchingProperty.NameAsUtf8Bytes !;
            jsonParameterInfo.InitializeDefaultValue(matchingProperty);
            return(jsonParameterInfo);
        }
Ejemplo n.º 3
0
        private static JsonParameterInfo CreateConstructorParameter(
            JsonParameterInfoValues parameterInfo,
            JsonPropertyInfo jsonPropertyInfo,
            bool sourceGenMode,
            JsonSerializerOptions options)
        {
            if (jsonPropertyInfo.IsIgnored)
            {
                return(JsonParameterInfo.CreateIgnoredParameterPlaceholder(parameterInfo, jsonPropertyInfo, sourceGenMode));
            }

            JsonConverter     converter         = jsonPropertyInfo.ConverterBase;
            JsonParameterInfo jsonParameterInfo = converter.CreateJsonParameterInfo();

            jsonParameterInfo.Initialize(parameterInfo, jsonPropertyInfo, options);

            return(jsonParameterInfo);
        }
Ejemplo n.º 4
0
        /// <summary>
        /// Create a parameter that is ignored at run time. It uses the same type (typeof(sbyte)) to help
        /// prevent issues with unsupported types and helps ensure we don't accidently (de)serialize it.
        /// </summary>
        public static JsonParameterInfo CreateIgnoredParameterPlaceholder(
            JsonParameterInfoValues parameterInfo,
            JsonPropertyInfo matchingProperty,
            bool sourceGenMode)
        {
            JsonParameterInfo jsonParameterInfo = new JsonParameterInfo <sbyte>();

            jsonParameterInfo.ClrInfo         = parameterInfo;
            jsonParameterInfo.PropertyType    = matchingProperty.PropertyType;
            jsonParameterInfo.NameAsUtf8Bytes = matchingProperty.NameAsUtf8Bytes !;

            // TODO: https://github.com/dotnet/runtime/issues/60082.
            // Default value initialization for params mapping to ignored properties doesn't
            // account for the default value of optional parameters. This should be fixed.

            if (sourceGenMode)
            {
                // The <T> value in the matching JsonPropertyInfo<T> instance matches the parameter type.
                jsonParameterInfo.DefaultValue = matchingProperty.DefaultValue;
            }
            else
            {
                // The <T> value in the created JsonPropertyInfo<T> instance (sbyte)
                // doesn't match the parameter type, use reflection to get the default value.
                Type parameterType = parameterInfo.ParameterType;

                DefaultValueHolder holder;
                if (matchingProperty.Options.TryGetJsonTypeInfo(parameterType, out JsonTypeInfo? typeInfo))
                {
                    holder = typeInfo.DefaultValueHolder;
                }
                else
                {
                    holder = DefaultValueHolder.CreateHolder(parameterInfo.ParameterType);
                }

                jsonParameterInfo.DefaultValue = holder.DefaultValue;
            }

            return(jsonParameterInfo);
        }