示例#1
0
        private static JsonPropertyInfo?GetPropertyWithUniqueAttribute(Type classType, Type attributeType, Dictionary <string, JsonPropertyInfo> cache)
        {
            JsonPropertyInfo?property = null;

            foreach (JsonPropertyInfo jsonPropertyInfo in cache.Values)
            {
                Debug.Assert(jsonPropertyInfo.PropertyInfo != null);
                Attribute?attribute = jsonPropertyInfo.PropertyInfo.GetCustomAttribute(attributeType);
                if (attribute != null)
                {
                    if (property != null)
                    {
                        ThrowHelper.ThrowInvalidOperationException_SerializationDuplicateTypeAttribute(classType, attributeType);
                    }

                    property = jsonPropertyInfo;
                }
            }

            return(property);
        }
示例#2
0
        private void DeterminePropertyName()
        {
            if (PropertyInfo == null)
            {
                return;
            }

            JsonPropertyNameAttribute?nameAttribute = GetAttribute <JsonPropertyNameAttribute>(PropertyInfo);

            if (nameAttribute != null)
            {
                string name = nameAttribute.Name;
                if (name == null)
                {
                    ThrowHelper.ThrowInvalidOperationException_SerializerPropertyNameNull(ParentClassType, this);
                }

                NameAsString = name;
            }
            else if (Options.PropertyNamingPolicy != null)
            {
                string name = Options.PropertyNamingPolicy.ConvertName(PropertyInfo.Name);
                if (name == null)
                {
                    ThrowHelper.ThrowInvalidOperationException_SerializerPropertyNameNull(ParentClassType, this);
                }

                NameAsString = name;
            }
            else
            {
                NameAsString = PropertyInfo.Name;
            }

            Debug.Assert(NameAsString != null);

            NameAsUtf8Bytes    = Encoding.UTF8.GetBytes(NameAsString);
            EscapedNameSection = JsonHelpers.GetEscapedPropertyNameSection(NameAsUtf8Bytes, Options.Encoder);
        }
        public void Write(ref WriteStack state, Utf8JsonWriter writer)
        {
            Debug.Assert(ShouldSerialize);

            if (state.Current.CollectionEnumerator != null)
            {
                // Forward the setter to the value-based JsonPropertyInfo.
                JsonPropertyInfo propertyInfo = ElementClassInfo.PolicyProperty;
                propertyInfo.WriteEnumerable(ref state, writer);
            }
            else
            {
                int originalDepth = writer.CurrentDepth;

                OnWrite(ref state.Current, writer);

                if (originalDepth != writer.CurrentDepth)
                {
                    ThrowHelper.ThrowJsonException_SerializationConverterWrite(state.PropertyPath, ConverterBase.ToString());
                }
            }
        }
示例#4
0
        private JsonPropertyInfo GetPropertyThatHasAttribute(Type attributeType)
        {
            Debug.Assert(PropertyCache != null);

            JsonPropertyInfo property = null;

            foreach (JsonPropertyInfo jsonPropertyInfo in PropertyCache.Values)
            {
                Attribute attribute = jsonPropertyInfo.PropertyInfo.GetCustomAttribute(attributeType);
                if (attribute != null)
                {
                    if (property != null)
                    {
                        ThrowHelper.ThrowInvalidOperationException_SerializationDuplicateTypeAttribute(Type, attributeType);
                    }

                    property = jsonPropertyInfo;
                }
            }

            return(property);
        }
示例#5
0
        public static object CreateDictionaryValue(ref ReadStack state)
        {
            JsonPropertyInfo jsonPropertyInfo = state.Current.JsonPropertyInfo;

            // If the property has a DictionaryConverter, then we use tempDictionaryValues.
            if (jsonPropertyInfo.DictionaryConverter != null)
            {
                IDictionary   converterDictionary;
                JsonClassInfo elementClassInfo = jsonPropertyInfo.ElementClassInfo;
                if (elementClassInfo.ClassType == ClassType.Value)
                {
                    converterDictionary = elementClassInfo.PolicyProperty.CreateConverterDictionary();
                }
                else
                {
                    converterDictionary = new Dictionary <string, object>();
                }

                state.Current.TempDictionaryValues = converterDictionary;

                // Clear the value if present to ensure we don't confuse TempDictionaryValues with the collection.
                if (!jsonPropertyInfo.IsPropertyPolicy && jsonPropertyInfo.CanBeNull)
                {
                    jsonPropertyInfo.SetValueAsObject(state.Current.ReturnValue, null);
                }

                return(null);
            }

            JsonClassInfo runtimeClassInfo = jsonPropertyInfo.RuntimeClassInfo;

            if (runtimeClassInfo.CreateObject == null)
            {
                ThrowHelper.ThrowNotSupportedException_DeserializeCreateObjectDelegateIsNull(jsonPropertyInfo.DeclaredPropertyType);
            }

            return(runtimeClassInfo.CreateObject());
        }
示例#6
0
        public static object CreateEnumerableValue(ref Utf8JsonReader reader, ref ReadStack state, JsonSerializerOptions options)
        {
            JsonPropertyInfo jsonPropertyInfo = state.Current.JsonPropertyInfo;

            // If the property has an EnumerableConverter, then we use tempEnumerableValues.
            if (jsonPropertyInfo.EnumerableConverter != null)
            {
                IList converterList;
                if (jsonPropertyInfo.ElementClassInfo.ClassType == ClassType.Value)
                {
                    converterList = jsonPropertyInfo.ElementClassInfo.GetPolicyProperty().CreateConverterList();
                }
                else
                {
                    converterList = new List <object>();
                }

                state.Current.TempEnumerableValues = converterList;

                return(null);
            }

            Type propType = state.Current.JsonPropertyInfo.RuntimePropertyType;

            if (typeof(IList).IsAssignableFrom(propType))
            {
                // If IList, add the members as we create them.
                JsonClassInfo collectionClassInfo = state.Current.JsonPropertyInfo.RuntimeClassInfo;
                IList         collection          = (IList)collectionClassInfo.CreateObject();
                return(collection);
            }
            else
            {
                ThrowHelper.ThrowJsonException_DeserializeUnableToConvertValue(propType, reader, state.JsonPath);
                return(null);
            }
        }
示例#7
0
        protected override void OnReadEnumerable(ref ReadStack state, ref Utf8JsonReader reader)
        {
            if (Converter == null)
            {
                ThrowHelper.ThrowJsonException_DeserializeUnableToConvertValue(RuntimePropertyType);
            }

            if (state.Current.KeyName == null && state.Current.IsProcessingDictionaryOrIDictionaryConstructible())
            {
                ThrowHelper.ThrowJsonException_DeserializeUnableToConvertValue(RuntimePropertyType);
                return;
            }

            // We need an initialized array in order to store the values.
            if (state.Current.IsProcessingEnumerable() && state.Current.TempEnumerableValues == null && state.Current.ReturnValue == null)
            {
                ThrowHelper.ThrowJsonException_DeserializeUnableToConvertValue(RuntimePropertyType);
                return;
            }

            TConverter value = Converter.Read(ref reader, RuntimePropertyType, Options);

            JsonSerializer.ApplyValueToEnumerable(ref value, ref state);
        }
示例#8
0
        public void DetermineEnumerablePopulationStrategy(object targetEnumerable)
        {
            Debug.Assert(JsonPropertyInfo.ClassType == ClassType.Enumerable);

            if (JsonPropertyInfo.RuntimeClassInfo.AddItemToObject != null)
            {
                if (!JsonPropertyInfo.TryCreateEnumerableAddMethod(targetEnumerable, out object addMethodDelegate))
                {
                    // No "add" method for this collection, hence, not supported for deserialization.
                    throw ThrowHelper.GetNotSupportedException_SerializationNotSupportedCollection(
                              JsonPropertyInfo.DeclaredPropertyType,
                              JsonPropertyInfo.ParentClassType,
                              JsonPropertyInfo.PropertyInfo);
                }

                AddObjectToEnumerable = addMethodDelegate;
            }
            else if (targetEnumerable is IList targetList)
            {
                if (targetList.IsReadOnly)
                {
                    throw ThrowHelper.GetNotSupportedException_SerializationNotSupportedCollection(
                              JsonPropertyInfo.DeclaredPropertyType,
                              JsonPropertyInfo.ParentClassType,
                              JsonPropertyInfo.PropertyInfo);
                }
            }
            // If there's no add method, and we can't cast to IList, this collection is not supported for deserialization.
            else
            {
                throw ThrowHelper.GetNotSupportedException_SerializationNotSupportedCollection(
                          JsonPropertyInfo.DeclaredPropertyType,
                          JsonPropertyInfo.ParentClassType,
                          JsonPropertyInfo.PropertyInfo);
            }
        }
示例#9
0
        public bool DetermineExtensionDataProperty(Dictionary <string, JsonPropertyInfo> cache)
        {
            JsonPropertyInfo?jsonPropertyInfo = GetPropertyWithUniqueAttribute(Type, typeof(JsonExtensionDataAttribute), cache);

            if (jsonPropertyInfo != null)
            {
                Type declaredPropertyType = jsonPropertyInfo.DeclaredPropertyType;
                if (typeof(IDictionary <string, object>).IsAssignableFrom(declaredPropertyType) ||
                    typeof(IDictionary <string, JsonElement>).IsAssignableFrom(declaredPropertyType))
                {
                    JsonConverter converter = Options.GetConverter(declaredPropertyType);
                    Debug.Assert(converter != null);
                }
                else
                {
                    ThrowHelper.ThrowInvalidOperationException_SerializationDataExtensionPropertyInvalid(Type, jsonPropertyInfo);
                }

                DataExtensionProperty = jsonPropertyInfo;
                return(true);
            }

            return(false);
        }
示例#10
0
        private static TValue?Read <TValue>(ref Utf8JsonReader reader, JsonTypeInfo jsonTypeInfo)
        {
            ReadStack state = default;

            state.Initialize(jsonTypeInfo);

            JsonReaderState readerState = reader.CurrentState;

            if (readerState.Options.CommentHandling == JsonCommentHandling.Allow)
            {
                throw new ArgumentException(SR.JsonSerializerDoesNotSupportComments, nameof(reader));
            }

            // Value copy to overwrite the ref on an exception and undo the destructive reads.
            Utf8JsonReader restore = reader;

            ReadOnlySpan <byte>     valueSpan     = default;
            ReadOnlySequence <byte> valueSequence = default;

            try
            {
                switch (reader.TokenType)
                {
                // A new reader was created and has never been read,
                // so we need to move to the first token.
                // (or a reader has terminated and we're about to throw)
                case JsonTokenType.None:
                // Using a reader loop the caller has identified a property they wish to
                // hydrate into a JsonDocument. Move to the value first.
                case JsonTokenType.PropertyName:
                {
                    if (!reader.Read())
                    {
                        ThrowHelper.ThrowJsonReaderException(ref reader, ExceptionResource.ExpectedOneCompleteToken);
                    }
                    break;
                }
                }

                switch (reader.TokenType)
                {
                // Any of the "value start" states are acceptable.
                case JsonTokenType.StartObject:
                case JsonTokenType.StartArray:
                {
                    long startingOffset = reader.TokenStartIndex;

                    if (!reader.TrySkip())
                    {
                        ThrowHelper.ThrowJsonReaderException(ref reader, ExceptionResource.NotEnoughData);
                    }

                    long totalLength = reader.BytesConsumed - startingOffset;
                    ReadOnlySequence <byte> sequence = reader.OriginalSequence;

                    if (sequence.IsEmpty)
                    {
                        valueSpan = reader.OriginalSpan.Slice(
                            checked ((int)startingOffset),
                            checked ((int)totalLength));
                    }
                    else
                    {
                        valueSequence = sequence.Slice(startingOffset, totalLength);
                    }

                    Debug.Assert(
                        reader.TokenType == JsonTokenType.EndObject ||
                        reader.TokenType == JsonTokenType.EndArray);

                    break;
                }

                // Single-token values
                case JsonTokenType.Number:
                case JsonTokenType.True:
                case JsonTokenType.False:
                case JsonTokenType.Null:
                {
                    if (reader.HasValueSequence)
                    {
                        valueSequence = reader.ValueSequence;
                    }
                    else
                    {
                        valueSpan = reader.ValueSpan;
                    }

                    break;
                }

                // String's ValueSequence/ValueSpan omits the quotes, we need them back.
                case JsonTokenType.String:
                {
                    ReadOnlySequence <byte> sequence = reader.OriginalSequence;

                    if (sequence.IsEmpty)
                    {
                        // Since the quoted string fit in a ReadOnlySpan originally
                        // the contents length plus the two quotes can't overflow.
                        int payloadLength = reader.ValueSpan.Length + 2;
                        Debug.Assert(payloadLength > 1);

                        ReadOnlySpan <byte> readerSpan = reader.OriginalSpan;

                        Debug.Assert(
                            readerSpan[(int)reader.TokenStartIndex] == (byte)'"',
                            $"Calculated span starts with {readerSpan[(int)reader.TokenStartIndex]}");

                        Debug.Assert(
                            readerSpan[(int)reader.TokenStartIndex + payloadLength - 1] == (byte)'"',
                            $"Calculated span ends with {readerSpan[(int)reader.TokenStartIndex + payloadLength - 1]}");

                        valueSpan = readerSpan.Slice((int)reader.TokenStartIndex, payloadLength);
                    }
                    else
                    {
                        long payloadLength = 2;

                        if (reader.HasValueSequence)
                        {
                            payloadLength += reader.ValueSequence.Length;
                        }
                        else
                        {
                            payloadLength += reader.ValueSpan.Length;
                        }

                        valueSequence = sequence.Slice(reader.TokenStartIndex, payloadLength);
                        Debug.Assert(
                            valueSequence.First.Span[0] == (byte)'"',
                            $"Calculated sequence starts with {valueSequence.First.Span[0]}");

                        Debug.Assert(
                            valueSequence.ToArray()[payloadLength - 1] == (byte)'"',
                            $"Calculated sequence ends with {valueSequence.ToArray()[payloadLength - 1]}");
                    }

                    break;
                }

                default:
                {
                    byte displayByte;

                    if (reader.HasValueSequence)
                    {
                        displayByte = reader.ValueSequence.First.Span[0];
                    }
                    else
                    {
                        displayByte = reader.ValueSpan[0];
                    }

                    ThrowHelper.ThrowJsonReaderException(
                        ref reader,
                        ExceptionResource.ExpectedStartOfValueNotFound,
                        displayByte);

                    break;
                }
                }
            }
            catch (JsonReaderException ex)
            {
                reader = restore;
                // Re-throw with Path information.
                ThrowHelper.ReThrowWithPath(ref state, ex);
            }

            int length = valueSpan.IsEmpty ? checked ((int)valueSequence.Length) : valueSpan.Length;

            byte[] rented = ArrayPool <byte> .Shared.Rent(length);

            Span <byte> rentedSpan = rented.AsSpan(0, length);

            try
            {
                if (valueSpan.IsEmpty)
                {
                    valueSequence.CopyTo(rentedSpan);
                }
                else
                {
                    valueSpan.CopyTo(rentedSpan);
                }

                JsonReaderOptions originalReaderOptions = readerState.Options;

                var newReader = new Utf8JsonReader(rentedSpan, originalReaderOptions);

                JsonConverter jsonConverter = state.Current.JsonPropertyInfo !.ConverterBase;
                TValue?       value         = ReadCore <TValue>(jsonConverter, ref newReader, jsonTypeInfo.Options, ref state);

                // The reader should have thrown if we have remaining bytes.
                Debug.Assert(newReader.BytesConsumed == length);

                return(value);
            }
            catch (JsonException)
            {
                reader = restore;
                throw;
            }
            finally
            {
                rentedSpan.Clear();
                ArrayPool <byte> .Shared.Return(rented);
            }
        }
示例#11
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;
            }
        }
示例#12
0
        private static void ReadCore(
            JsonSerializerOptions options,
            ref Utf8JsonReader reader,
            ref ReadStack readStack)
        {
            try
            {
                JsonReaderState initialState         = default;
                long            initialBytesConsumed = default;

                while (true)
                {
                    if (readStack.ReadAhead)
                    {
                        // When we're reading ahead we always have to save the state
                        // as we don't know if the next token is an opening object or
                        // array brace.
                        initialState         = reader.CurrentState;
                        initialBytesConsumed = reader.BytesConsumed;
                    }

                    if (!reader.Read())
                    {
                        // Need more data
                        break;
                    }

                    JsonTokenType tokenType = reader.TokenType;

                    if (JsonHelpers.IsInRangeInclusive(tokenType, JsonTokenType.String, JsonTokenType.False))
                    {
                        Debug.Assert(tokenType == JsonTokenType.String || tokenType == JsonTokenType.Number || tokenType == JsonTokenType.True || tokenType == JsonTokenType.False);

                        HandleValue(tokenType, options, ref reader, ref readStack);
                    }
                    else if (tokenType == JsonTokenType.PropertyName)
                    {
                        HandlePropertyName(options, ref reader, ref readStack);
                    }
                    else if (tokenType == JsonTokenType.StartObject)
                    {
                        if (readStack.Current.SkipProperty)
                        {
                            readStack.Push();
                            readStack.Current.Drain = true;
                        }
                        else if (readStack.Current.IsProcessingValue())
                        {
                            if (!HandleObjectAsValue(tokenType, options, ref reader, ref readStack, ref initialState, initialBytesConsumed))
                            {
                                // Need more data
                                break;
                            }
                        }
                        else if (readStack.Current.IsProcessingDictionary())
                        {
                            HandleStartDictionary(options, ref readStack);
                        }
                        else
                        {
                            HandleStartObject(options, ref readStack);
                        }
                    }
                    else if (tokenType == JsonTokenType.EndObject)
                    {
                        if (readStack.Current.Drain)
                        {
                            readStack.Pop();

                            // Clear the current property in case it is a dictionary, since dictionaries must have EndProperty() called when completed.
                            // A non-dictionary property can also have EndProperty() called when completed, although it is redundant.
                            readStack.Current.EndProperty();
                        }
                        else if (readStack.Current.IsProcessingDictionary())
                        {
                            HandleEndDictionary(options, ref readStack);
                        }
                        else
                        {
                            HandleEndObject(ref readStack);
                        }
                    }
                    else if (tokenType == JsonTokenType.StartArray)
                    {
                        if (!readStack.Current.IsProcessingValue())
                        {
                            HandleStartArray(options, ref readStack);
                        }
                        else if (!HandleObjectAsValue(tokenType, options, ref reader, ref readStack, ref initialState, initialBytesConsumed))
                        {
                            // Need more data
                            break;
                        }
                    }
                    else if (tokenType == JsonTokenType.EndArray)
                    {
                        HandleEndArray(options, ref readStack);
                    }
                    else if (tokenType == JsonTokenType.Null)
                    {
                        HandleNull(options, ref reader, ref readStack);
                    }
                }
            }
            catch (JsonReaderException ex)
            {
                // Re-throw with Path information.
                ThrowHelper.ReThrowWithPath(readStack, ex);
            }
            catch (FormatException ex) when(ex.Source == ThrowHelper.ExceptionSourceValueToRethrowAsJsonException)
            {
                ThrowHelper.ReThrowWithPath(readStack, reader, ex);
            }
            catch (InvalidOperationException ex) when(ex.Source == ThrowHelper.ExceptionSourceValueToRethrowAsJsonException)
            {
                ThrowHelper.ReThrowWithPath(readStack, reader, ex);
            }
            catch (JsonException ex)
            {
                ThrowHelper.AddExceptionInformation(readStack, reader, ex);
                throw;
            }

            readStack.BytesConsumed += reader.BytesConsumed;
        }
示例#13
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:
            {
                // Create the policy property.
                PropertyInfoForClassInfo = CreatePropertyInfoForClassInfo(type, runtimeType, converter !, options);

                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(Type, 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;

                if (converter.ConstructorIsParameterized)
                {
                    converter.CreateConstructorDelegate(options);
                    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();
            }
        }
示例#14
0
        // There are three conditions to consider for an object (primitive value, enumerable or object) being processed here:
        // 1) The object type was specified as the root-level return type to a Parse\Read method.
        // 2) The object is property on a parent object.
        // 3) The object is an element in an enumerable.
        private static bool Write(
            Utf8JsonWriter writer,
            int originalWriterDepth,
            int flushThreshold,
            JsonSerializerOptions options,
            ref WriteStack state)
        {
            bool finishedSerializing;

            try
            {
                do
                {
                    WriteStackFrame current = state.Current;
                    switch (current.JsonClassInfo.ClassType)
                    {
                    case ClassType.Enumerable:
                        finishedSerializing = HandleEnumerable(current.JsonClassInfo.ElementClassInfo, options, writer, ref state);
                        break;

                    case ClassType.Value:
                        Debug.Assert(current.JsonPropertyInfo.ClassType == ClassType.Value);
                        current.JsonPropertyInfo.Write(ref state, writer);
                        finishedSerializing = true;
                        break;

                    case ClassType.Object:
                        finishedSerializing = WriteObject(options, writer, ref state);
                        break;

                    case ClassType.Dictionary:
                    case ClassType.IDictionaryConstructible:
                        finishedSerializing = HandleDictionary(current.JsonClassInfo.ElementClassInfo, options, writer, ref state);
                        break;

                    default:
                        Debug.Assert(state.Current.JsonClassInfo.ClassType == ClassType.Unknown);

                        // Treat typeof(object) as an empty object.
                        finishedSerializing = WriteObject(options, writer, ref state);
                        break;
                    }

                    if (finishedSerializing)
                    {
                        if (writer.CurrentDepth == 0 || writer.CurrentDepth == originalWriterDepth)
                        {
                            break;
                        }
                    }
                    else if (writer.CurrentDepth >= options.EffectiveMaxDepth)
                    {
                        ThrowHelper.ThrowJsonException_DepthTooLarge(writer.CurrentDepth, state, options);
                    }

                    // If serialization is not yet end and we surpass beyond flush threshold return false and flush stream.
                    if (flushThreshold >= 0 && writer.BytesPending > flushThreshold)
                    {
                        return(false);
                    }
                } while (true);
            }
            catch (InvalidOperationException ex) when(ex.Source == ThrowHelper.ExceptionSourceValueToRethrowAsJsonException)
            {
                ThrowHelper.ReThrowWithPath(state, ex);
            }
            catch (JsonException ex)
            {
                ThrowHelper.AddExceptionInformation(state, ex);
                throw;
            }

            return(true);
        }
示例#15
0
        private void InitializeConstructorParameters(ConstructorInfo constructorInfo)
        {
            ParameterInfo[] parameters = constructorInfo !.GetParameters();
            Dictionary <string, JsonParameterInfo> parameterCache = CreateParameterCache(parameters.Length, Options);

            Dictionary <string, JsonPropertyInfo> propertyCache = PropertyCache !;

            foreach (ParameterInfo parameterInfo in parameters)
            {
                PropertyInfo?firstMatch = null;
                bool         isBound    = false;

                foreach (JsonPropertyInfo jsonPropertyInfo in PropertyCacheArray !)
                {
                    // This is not null because it is an actual
                    // property on a type, not a "policy property".
                    PropertyInfo propertyInfo = jsonPropertyInfo.PropertyInfo !;

                    string camelCasePropName = JsonNamingPolicy.CamelCase.ConvertName(propertyInfo.Name);

                    if (parameterInfo.Name == camelCasePropName &&
                        parameterInfo.ParameterType == propertyInfo.PropertyType)
                    {
                        if (isBound)
                        {
                            Debug.Assert(firstMatch != null);

                            // Multiple object properties cannot bind to the same
                            // constructor parameter.
                            ThrowHelper.ThrowInvalidOperationException_MultiplePropertiesBindToConstructorParameters(
                                Type,
                                parameterInfo,
                                firstMatch,
                                propertyInfo,
                                constructorInfo);
                        }

                        JsonParameterInfo jsonParameterInfo = AddConstructorParameter(parameterInfo, jsonPropertyInfo, Options);

                        // One object property cannot map to multiple constructor
                        // parameters (ConvertName above can't return multiple strings).
                        parameterCache.Add(jsonParameterInfo.NameAsString, jsonParameterInfo);

                        // Remove property from deserialization cache to reduce the number of JsonPropertyInfos considered during JSON matching.
                        propertyCache.Remove(jsonPropertyInfo.NameAsString !);

                        isBound    = true;
                        firstMatch = propertyInfo;
                    }
                }
            }

            // It is invalid for the extension data property to bind with a constructor argument.
            if (DataExtensionProperty != null &&
                parameterCache.ContainsKey(DataExtensionProperty.NameAsString !))
            {
                ThrowHelper.ThrowInvalidOperationException_ExtensionDataCannotBindToCtorParam(DataExtensionProperty.PropertyInfo !, Type, constructorInfo);
            }

            ParameterCache = parameterCache;
            ParameterCount = parameters.Length;

            PropertyCache = propertyCache;
        }
示例#16
0
        internal static void Unescape(ReadOnlySpan <byte> source, Span <byte> destination, int idx, out int written)
        {
            Debug.Assert(idx >= 0 && idx < source.Length);
            Debug.Assert(source[idx] == JsonConstants.BackSlash);
            Debug.Assert(destination.Length >= source.Length);

            source.Slice(0, idx).CopyTo(destination);
            written = idx;

            for (; idx < source.Length; idx++)
            {
                byte currentByte = source[idx];
                if (currentByte == JsonConstants.BackSlash)
                {
                    idx++;
                    currentByte = source[idx];

                    if (currentByte == JsonConstants.Quote)
                    {
                        destination[written++] = JsonConstants.Quote;
                    }
                    else if (currentByte == 'n')
                    {
                        destination[written++] = JsonConstants.LineFeed;
                    }
                    else if (currentByte == 'r')
                    {
                        destination[written++] = JsonConstants.CarriageReturn;
                    }
                    else if (currentByte == JsonConstants.BackSlash)
                    {
                        destination[written++] = JsonConstants.BackSlash;
                    }
                    else if (currentByte == JsonConstants.Slash)
                    {
                        destination[written++] = JsonConstants.Slash;
                    }
                    else if (currentByte == 't')
                    {
                        destination[written++] = JsonConstants.Tab;
                    }
                    else if (currentByte == 'b')
                    {
                        destination[written++] = JsonConstants.BackSpace;
                    }
                    else if (currentByte == 'f')
                    {
                        destination[written++] = JsonConstants.FormFeed;
                    }
                    else if (currentByte == 'u')
                    {
                        // The source is known to be valid JSON, and hence if we see a \u, it is guaranteed to have 4 hex digits following it
                        // Otherwise, the Utf8JsonReader would have alreayd thrown an exception.
                        Debug.Assert(source.Length >= idx + 5);

                        bool result = Utf8Parser.TryParse(source.Slice(idx + 1, 4), out int scalar, out int bytesConsumed, 'x');
                        Debug.Assert(result);
                        Debug.Assert(bytesConsumed == 4);
                        idx += bytesConsumed;     // The loop iteration will increment idx past the last hex digit

                        if (JsonHelpers.IsInRangeInclusive((uint)scalar, JsonConstants.HighSurrogateStartValue, JsonConstants.LowSurrogateEndValue))
                        {
                            // The first hex value cannot be a low surrogate.
                            if (scalar >= JsonConstants.LowSurrogateStartValue)
                            {
                                ThrowHelper.ThrowInvalidOperationException_ReadInvalidUTF16(scalar);
                            }

                            Debug.Assert(JsonHelpers.IsInRangeInclusive((uint)scalar, JsonConstants.HighSurrogateStartValue, JsonConstants.HighSurrogateEndValue));

                            idx += 3;   // Skip the last hex digit and the next \u

                            // We must have a low surrogate following a high surrogate.
                            if (source.Length < idx + 4 || source[idx - 2] != '\\' || source[idx - 1] != 'u')
                            {
                                ThrowHelper.ThrowInvalidOperationException_ReadInvalidUTF16();
                            }

                            // The source is known to be valid JSON, and hence if we see a \u, it is guaranteed to have 4 hex digits following it
                            // Otherwise, the Utf8JsonReader would have alreayd thrown an exception.
                            result = Utf8Parser.TryParse(source.Slice(idx, 4), out int lowSurrogate, out bytesConsumed, 'x');
                            Debug.Assert(result);
                            Debug.Assert(bytesConsumed == 4);

                            // If the first hex value is a high surrogate, the next one must be a low surrogate.
                            if (!JsonHelpers.IsInRangeInclusive((uint)lowSurrogate, JsonConstants.LowSurrogateStartValue, JsonConstants.LowSurrogateEndValue))
                            {
                                ThrowHelper.ThrowInvalidOperationException_ReadInvalidUTF16(lowSurrogate);
                            }

                            idx += bytesConsumed - 1;  // The loop iteration will increment idx past the last hex digit

                            // To find the unicode scalar:
                            // (0x400 * (High surrogate - 0xD800)) + Low surrogate - 0xDC00 + 0x10000
                            scalar = (JsonConstants.BitShiftBy10 * (scalar - JsonConstants.HighSurrogateStartValue))
                                     + (lowSurrogate - JsonConstants.LowSurrogateStartValue)
                                     + JsonConstants.UnicodePlane01StartValue;
                        }

#if BUILDING_INBOX_LIBRARY
                        var rune         = new Rune(scalar);
                        int bytesWritten = rune.EncodeToUtf8(destination.Slice(written));
#else
                        EncodeToUtf8Bytes((uint)scalar, destination.Slice(written), out int bytesWritten);
#endif
                        Debug.Assert(bytesWritten <= 4);
                        written += bytesWritten;
                    }
                }
                else
                {
                    destination[written++] = currentByte;
                }
            }
        }
示例#17
0
        // If this method is changed, also change ApplyValueToEnumerable.
        internal static void ApplyObjectToEnumerable(
            object value,
            ref ReadStack state,
            ref Utf8JsonReader reader,
            bool setPropertyDirectly = false)
        {
            Debug.Assert(!state.Current.SkipProperty);

            if (state.Current.IsEnumerable)
            {
                if (state.Current.TempEnumerableValues != null)
                {
                    state.Current.TempEnumerableValues.Add(value);
                }
                else
                {
                    if (!(state.Current.ReturnValue is IList list))
                    {
                        ThrowHelper.ThrowJsonException_DeserializeUnableToConvertValue(value.GetType(), reader, state.JsonPath);
                        return;
                    }
                    list.Add(value);
                }
            }
            else if (!setPropertyDirectly && state.Current.IsEnumerableProperty)
            {
                Debug.Assert(state.Current.JsonPropertyInfo != null);
                Debug.Assert(state.Current.ReturnValue != null);
                if (state.Current.TempEnumerableValues != null)
                {
                    state.Current.TempEnumerableValues.Add(value);
                }
                else
                {
                    IList list = (IList)state.Current.JsonPropertyInfo.GetValueAsObject(state.Current.ReturnValue);
                    if (list == null)
                    {
                        state.Current.JsonPropertyInfo.SetValueAsObject(state.Current.ReturnValue, value);
                    }
                    else
                    {
                        list.Add(value);
                    }
                }
            }
            else if (state.Current.IsDictionary || (state.Current.IsDictionaryProperty && !setPropertyDirectly))
            {
                Debug.Assert(state.Current.ReturnValue != null);
                IDictionary dictionary = (IDictionary)state.Current.JsonPropertyInfo.GetValueAsObject(state.Current.ReturnValue);

                string key = state.Current.KeyName;
                Debug.Assert(!string.IsNullOrEmpty(key));
                dictionary[key] = value;
            }
            else if (state.Current.IsIDictionaryConstructible ||
                     (state.Current.IsIDictionaryConstructibleProperty && !setPropertyDirectly))
            {
                Debug.Assert(state.Current.TempDictionaryValues != null);
                IDictionary dictionary = (IDictionary)state.Current.JsonPropertyInfo.GetValueAsObject(state.Current.TempDictionaryValues);

                string key = state.Current.KeyName;
                Debug.Assert(!string.IsNullOrEmpty(key));
                dictionary[key] = value;
            }
            else
            {
                Debug.Assert(state.Current.JsonPropertyInfo != null);
                state.Current.JsonPropertyInfo.SetValueAsObject(state.Current.ReturnValue, value);
            }
        }
示例#18
0
        /// <summary>
        /// Returns true if successful, false is the reader ran out of buffer.
        /// Sets state.Current.ReturnValue to the $ref target for MetadataRefProperty cases.
        /// </summary>
        internal static bool ResolveMetadata(
            JsonConverter converter,
            ref Utf8JsonReader reader,
            ref ReadStack state)
        {
            if (state.Current.ObjectState < StackFrameObjectState.ReadAheadNameOrEndObject)
            {
                // Read the first metadata property name.
                if (!ReadAheadMetataDataAndSetState(ref reader, ref state, StackFrameObjectState.ReadNameOrEndObject))
                {
                    return(false);
                }
            }

            if (state.Current.ObjectState == StackFrameObjectState.ReadNameOrEndObject)
            {
                if (reader.TokenType != JsonTokenType.PropertyName)
                {
                    // An enumerable needs metadata since it starts with StartObject.
                    if (converter.ClassType == ClassType.Enumerable)
                    {
                        ThrowHelper.ThrowJsonException_MetadataPreservedArrayValuesNotFound(converter.TypeToConvert);
                    }

                    // The reader should have detected other invalid cases.
                    Debug.Assert(reader.TokenType == JsonTokenType.EndObject);

                    // Skip the read of the first property name, since we already read it above.
                    state.Current.PropertyState = StackFramePropertyState.ReadName;

                    return(true);
                }

                ReadOnlySpan <byte>  propertyName = reader.GetSpan();
                MetadataPropertyName metadata     = GetMetadataPropertyName(propertyName);
                if (metadata == MetadataPropertyName.Id)
                {
                    state.Current.JsonPropertyName = propertyName.ToArray();
                    if (!converter.CanHaveIdMetadata)
                    {
                        ThrowHelper.ThrowJsonException_MetadataCannotParsePreservedObjectIntoImmutable(converter.TypeToConvert);
                    }

                    state.Current.ObjectState = StackFrameObjectState.ReadAheadIdValue;
                }
                else if (metadata == MetadataPropertyName.Ref)
                {
                    state.Current.JsonPropertyName = propertyName.ToArray();
                    if (converter.IsValueType)
                    {
                        ThrowHelper.ThrowJsonException_MetadataInvalidReferenceToValueType(converter.TypeToConvert);
                    }

                    state.Current.ObjectState = StackFrameObjectState.ReadAheadRefValue;
                }
                else if (metadata == MetadataPropertyName.Values)
                {
                    state.Current.JsonPropertyName = propertyName.ToArray();
                    if (converter.ClassType == ClassType.Enumerable)
                    {
                        ThrowHelper.ThrowJsonException_MetadataMissingIdBeforeValues();
                    }
                    else
                    {
                        ThrowHelper.ThrowJsonException_MetadataInvalidPropertyWithLeadingDollarSign(propertyName, ref state, reader);
                    }
                }
                else
                {
                    Debug.Assert(metadata == MetadataPropertyName.NoMetadata);

                    // Having a StartObject without metadata properties is not allowed.
                    if (converter.ClassType == ClassType.Enumerable)
                    {
                        state.Current.JsonPropertyName = propertyName.ToArray();
                        ThrowHelper.ThrowJsonException_MetadataPreservedArrayInvalidProperty(converter.TypeToConvert, reader);
                    }

                    // Skip the read of the first property name, since we already read it above.
                    state.Current.PropertyState = StackFramePropertyState.ReadName;
                    return(true);
                }
            }

            if (state.Current.ObjectState == StackFrameObjectState.ReadAheadRefValue)
            {
                if (!ReadAheadMetataDataAndSetState(ref reader, ref state, StackFrameObjectState.ReadRefValue))
                {
                    return(false);
                }
            }
            else if (state.Current.ObjectState == StackFrameObjectState.ReadAheadIdValue)
            {
                if (!ReadAheadMetataDataAndSetState(ref reader, ref state, StackFrameObjectState.ReadIdValue))
                {
                    return(false);
                }
            }

            if (state.Current.ObjectState == StackFrameObjectState.ReadRefValue)
            {
                if (reader.TokenType != JsonTokenType.String)
                {
                    ThrowHelper.ThrowJsonException_MetadataValueWasNotString(reader.TokenType);
                }

                string key = reader.GetString() !;

                // todo: https://github.com/dotnet/runtime/issues/32354
                state.Current.ReturnValue = state.ReferenceResolver.ResolveReferenceOnDeserialize(key);
                state.Current.ObjectState = StackFrameObjectState.ReadAheadRefEndObject;
            }
            else if (state.Current.ObjectState == StackFrameObjectState.ReadIdValue)
            {
                if (reader.TokenType != JsonTokenType.String)
                {
                    ThrowHelper.ThrowJsonException_MetadataValueWasNotString(reader.TokenType);
                }

                state.Current.MetadataId = reader.GetString();

                // Clear the MetadataPropertyName since we are done processing Id.
                state.Current.JsonPropertyName = default;

                if (converter.ClassType == ClassType.Enumerable)
                {
                    // Need to Read $values property name.
                    state.Current.ObjectState = StackFrameObjectState.ReadAheadValuesName;
                }
                else
                {
                    // We are done reading metadata.
                    state.Current.ObjectState = StackFrameObjectState.PropertyValue;
                    return(true);
                }
            }

            if (state.Current.ObjectState == StackFrameObjectState.ReadAheadRefEndObject)
            {
                if (!ReadAheadMetataDataAndSetState(ref reader, ref state, StackFrameObjectState.ReadRefEndObject))
                {
                    return(false);
                }
            }

            if (state.Current.ObjectState == StackFrameObjectState.ReadRefEndObject)
            {
                if (reader.TokenType != JsonTokenType.EndObject)
                {
                    // We just read a property. The only valid next tokens are EndObject and PropertyName.
                    Debug.Assert(reader.TokenType == JsonTokenType.PropertyName);

                    ThrowHelper.ThrowJsonException_MetadataReferenceObjectCannotContainOtherProperties(reader.GetSpan(), ref state);
                }

                return(true);
            }

            if (state.Current.ObjectState == StackFrameObjectState.ReadAheadValuesName)
            {
                if (!ReadAheadMetataDataAndSetState(ref reader, ref state, StackFrameObjectState.ReadValuesName))
                {
                    return(false);
                }
            }

            if (state.Current.ObjectState == StackFrameObjectState.ReadValuesName)
            {
                if (reader.TokenType != JsonTokenType.PropertyName)
                {
                    ThrowHelper.ThrowJsonException_MetadataPreservedArrayValuesNotFound(converter.TypeToConvert);
                }

                ReadOnlySpan <byte> propertyName = reader.GetSpan();

                // Remember the property in case we get an exception.
                state.Current.JsonPropertyName = propertyName.ToArray();

                if (GetMetadataPropertyName(propertyName) != MetadataPropertyName.Values)
                {
                    ThrowHelper.ThrowJsonException_MetadataPreservedArrayInvalidProperty(converter.TypeToConvert, reader);
                }

                state.Current.ObjectState = StackFrameObjectState.ReadAheadValuesStartArray;
            }

            if (state.Current.ObjectState == StackFrameObjectState.ReadAheadValuesStartArray)
            {
                if (!ReadAheadMetataDataAndSetState(ref reader, ref state, StackFrameObjectState.ReadValuesStartArray))
                {
                    return(false);
                }
            }

            if (state.Current.ObjectState == StackFrameObjectState.ReadValuesStartArray)
            {
                if (reader.TokenType != JsonTokenType.StartArray)
                {
                    ThrowHelper.ThrowJsonException_MetadataValuesInvalidToken(reader.TokenType);
                }

                state.Current.ObjectState = StackFrameObjectState.PropertyValue;
            }

            return(true);
        }
        internal JsonConverter GetConverterInternal(Type typeToConvert)
        {
            Debug.Assert(typeToConvert != null);

            if (_converters.TryGetValue(typeToConvert, out JsonConverter? converter))
            {
                Debug.Assert(converter != null);
                return(converter);
            }

            // Priority 1: If there is a JsonSerializerContext, fetch the converter from there.
            converter = _context?.GetTypeInfo(typeToConvert)?.PropertyInfoForTypeInfo?.ConverterBase;

            // Priority 2: Attempt to get custom converter added at runtime.
            // Currently there is not a way at runtime to override the [JsonConverter] when applied to a property.
            foreach (JsonConverter item in Converters)
            {
                if (item.CanConvert(typeToConvert))
                {
                    converter = item;
                    break;
                }
            }

            // Priority 3: Attempt to get converter from [JsonConverter] on the type being converted.
            if (converter == null)
            {
                JsonConverterAttribute?converterAttribute = (JsonConverterAttribute?)
                                                            GetAttributeThatCanHaveMultiple(typeToConvert, typeof(JsonConverterAttribute));

                if (converterAttribute != null)
                {
                    converter = GetConverterFromAttribute(converterAttribute, typeToConvert: typeToConvert, classTypeAttributeIsOn: typeToConvert, memberInfo: null);
                }
            }

            // Priority 4: Attempt to get built-in converter.
            if (converter == null)
            {
                if (s_defaultSimpleConverters == null || s_defaultFactoryConverters == null)
                {
                    // (De)serialization using serializer's options-based methods has not yet occurred, so the built-in converters are not rooted.
                    // Even though source-gen code paths do not call this method <i.e. JsonSerializerOptions.GetConverter(Type)>, we do not root all the
                    // built-in converters here since we fetch converters for any type included for source generation from the binded context (Priority 1).
                    Debug.Assert(s_defaultSimpleConverters == null);
                    Debug.Assert(s_defaultFactoryConverters == null);
                    ThrowHelper.ThrowNotSupportedException_BuiltInConvertersNotRooted(typeToConvert);
                    return(null !);
                }

                if (s_defaultSimpleConverters.TryGetValue(typeToConvert, out JsonConverter? foundConverter))
                {
                    Debug.Assert(foundConverter != null);
                    converter = foundConverter;
                }
                else
                {
                    foreach (JsonConverter item in s_defaultFactoryConverters)
                    {
                        if (item.CanConvert(typeToConvert))
                        {
                            converter = item;
                            break;
                        }
                    }

                    // Since the object and IEnumerable converters cover all types, we should have a converter.
                    Debug.Assert(converter != null);
                }
            }

            // Allow redirection for generic types or the enum converter.
            if (converter is JsonConverterFactory factory)
            {
                converter = factory.GetConverterInternal(typeToConvert, this);

                // A factory cannot return null; GetConverterInternal checked for that.
                Debug.Assert(converter != null);
            }

            Type converterTypeToConvert = converter.TypeToConvert;

            if (!converterTypeToConvert.IsAssignableFromInternal(typeToConvert) &&
                !typeToConvert.IsAssignableFromInternal(converterTypeToConvert))
            {
                ThrowHelper.ThrowInvalidOperationException_SerializationConverterNotCompatible(converter.GetType(), typeToConvert);
            }

            // Only cache the value once (de)serialization has occurred since new converters can be added that may change the result.
            if (_haveTypesBeenCreated)
            {
                // A null converter is allowed here and cached.

                // Ignore failure case here in multi-threaded cases since the cached item will be equivalent.
                _converters.TryAdd(typeToConvert, converter);
            }

            return(converter);
        }
        /// <summary>
        /// Returns the converter for the specified type.
        /// </summary>
        /// <param name="typeToConvert">The type to return a converter for.</param>
        /// <returns>
        /// The converter for the given type.
        /// </returns>
        /// <exception cref="InvalidOperationException">
        /// The configured <see cref="JsonConverter"/> for <paramref name="typeToConvert"/> returned an invalid converter.
        /// </exception>
        /// <exception cref="NotSupportedException">
        /// There is no compatible <see cref="System.Text.Json.Serialization.JsonConverter"/>
        /// for <paramref name="typeToConvert"/> or its serializable members.
        /// </exception>
        public JsonConverter GetConverter(Type typeToConvert)
        {
            if (_converters.TryGetValue(typeToConvert, out JsonConverter? converter))
            {
                Debug.Assert(converter != null);
                return(converter);
            }

            // Priority 2: Attempt to get custom converter added at runtime.
            // Currently there is not a way at runtime to overide the [JsonConverter] when applied to a property.
            foreach (JsonConverter item in Converters)
            {
                if (item.CanConvert(typeToConvert))
                {
                    converter = item;
                    break;
                }
            }

            // Priority 3: Attempt to get converter from [JsonConverter] on the type being converted.
            if (converter == null)
            {
                JsonConverterAttribute?converterAttribute = (JsonConverterAttribute?)
                                                            GetAttributeThatCanHaveMultiple(typeToConvert, typeof(JsonConverterAttribute));

                if (converterAttribute != null)
                {
                    converter = GetConverterFromAttribute(converterAttribute, typeToConvert: typeToConvert, classTypeAttributeIsOn: typeToConvert, memberInfo: null);
                }
            }

            // Priority 4: Attempt to get built-in converter.
            if (converter == null)
            {
                if (s_defaultSimpleConverters.TryGetValue(typeToConvert, out JsonConverter? foundConverter))
                {
                    Debug.Assert(foundConverter != null);
                    converter = foundConverter;
                }
                else
                {
                    foreach (JsonConverter item in s_defaultFactoryConverters)
                    {
                        if (item.CanConvert(typeToConvert))
                        {
                            converter = item;
                            break;
                        }
                    }

                    // Since the object and IEnumerable converters cover all types, we should have a converter.
                    Debug.Assert(converter != null);
                }
            }

            // Allow redirection for generic types or the enum converter.
            if (converter is JsonConverterFactory factory)
            {
                converter = factory.GetConverterInternal(typeToConvert, this);

                // A factory cannot return null; GetConverterInternal checked for that.
                Debug.Assert(converter != null);
            }

            Type converterTypeToConvert = converter.TypeToConvert;

            if (!converterTypeToConvert.IsAssignableFromInternal(typeToConvert) &&
                !typeToConvert.IsAssignableFromInternal(converterTypeToConvert))
            {
                ThrowHelper.ThrowInvalidOperationException_SerializationConverterNotCompatible(converter.GetType(), typeToConvert);
            }

            // Only cache the value once (de)serialization has occurred since new converters can be added that may change the result.
            if (_haveTypesBeenCreated)
            {
                // A null converter is allowed here and cached.

                // Ignore failure case here in multi-threaded cases since the cached item will be equivalent.
                _converters.TryAdd(typeToConvert, converter);
            }

            return(converter);
        }
示例#21
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;
                        }

                        // 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.
                            }
                        }
                        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();
            }
        }
示例#22
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;
示例#23
0
        public JsonClassInfo(Type type, JsonSerializerOptions options)
        {
            Type    = type;
            Options = options;

            JsonConverter converter = GetConverter(
                Type,
                parentClassType: null, // A ClassInfo never has a "parent" class.
                memberInfo: null,      // A ClassInfo never has a "parent" property.
                out Type runtimeType,
                Options);

            ClassType = converter.ClassType;
            JsonNumberHandling?typeNumberHandling = GetNumberHandlingForType(Type);

            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, MemberInfo>?ignoredMembers = null;

                // We start from the most derived type.
                for (Type?currentType = type; currentType != null; currentType = currentType.BaseType)
                {
                    const BindingFlags bindingFlags =
                        BindingFlags.Instance |
                        BindingFlags.Public |
                        BindingFlags.NonPublic |
                        BindingFlags.DeclaredOnly;

                    foreach (PropertyInfo propertyInfo in currentType.GetProperties(bindingFlags))
                    {
                        // Ignore indexers and virtual properties that have overrides that were [JsonIgnore]d.
                        if (propertyInfo.GetIndexParameters().Length > 0 || PropertyIsOverridenAndIgnored(propertyInfo, 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, typeNumberHandling, cache, ref ignoredMembers);
                        }
                        else
                        {
                            if (JsonPropertyInfo.GetAttribute <JsonIncludeAttribute>(propertyInfo) != null)
                            {
                                ThrowHelper.ThrowInvalidOperationException_JsonIncludeOnNonPublicInvalid(propertyInfo, currentType);
                            }

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

                    foreach (FieldInfo fieldInfo in currentType.GetFields(bindingFlags))
                    {
                        if (PropertyIsOverridenAndIgnored(fieldInfo, ignoredMembers))
                        {
                            continue;
                        }

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

                        if (fieldInfo.IsPublic)
                        {
                            if (hasJsonInclude || Options.IncludeFields)
                            {
                                CacheMember(currentType, fieldInfo.FieldType, fieldInfo, typeNumberHandling, cache, ref ignoredMembers);
                            }
                        }
                        else
                        {
                            if (hasJsonInclude)
                            {
                                ThrowHelper.ThrowInvalidOperationException_JsonIncludeOnNonPublicInvalid(fieldInfo, currentType);
                            }

                            // Non-public fields 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);

                // These are not accessed by other threads until the current JsonClassInfo instance
                // is finished initializing and added to the cache on JsonSerializerOptions.
                PropertyCache      = cache;
                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(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();
            }
        }
示例#24
0
        public T?SetValue(string propertyName, T?value, Action?assignParent = null)
        {
            if (IsReadOnly)
            {
                ThrowHelper.ThrowNotSupportedException_NodeCollectionIsReadOnly();
            }

            if (propertyName == null)
            {
                throw new ArgumentNullException(nameof(propertyName));
            }

            CreateDictionaryIfThresholdMet();

            T?existing = null;

            if (_propertyDictionary != null)
            {
                // Fast path if item doesn't exist in dictionary.
                if (JsonHelpers.TryAdd(_propertyDictionary, propertyName, value))
                {
                    assignParent?.Invoke();
                    _propertyList.Add(new KeyValuePair <string, T?>(propertyName, value));
                    return(null);
                }

                existing = _propertyDictionary[propertyName];
                if (ReferenceEquals(existing, value))
                {
                    // Ignore if the same value.
                    return(null);
                }
            }

            int i = FindValueIndex(propertyName);

            if (i >= 0)
            {
                if (_propertyDictionary != null)
                {
                    _propertyDictionary[propertyName] = value;
                }
                else
                {
                    KeyValuePair <string, T?> current = _propertyList[i];
                    if (ReferenceEquals(current.Value, value))
                    {
                        // Ignore if the same value.
                        return(null);
                    }

                    existing = current.Value;
                }

                assignParent?.Invoke();
                _propertyList[i] = new KeyValuePair <string, T?>(propertyName, value);
            }
            else
            {
                assignParent?.Invoke();
                _propertyDictionary?.Add(propertyName, value);
                _propertyList.Add(new KeyValuePair <string, T?>(propertyName, value));
                Debug.Assert(existing == null);
            }

            return(existing);
        }
        private static void HandleStartDictionary(JsonSerializerOptions options, ref ReadStack state)
        {
            Debug.Assert(!state.Current.IsProcessingEnumerable());

            JsonPropertyInfo jsonPropertyInfo = state.Current.JsonPropertyInfo;

            if (jsonPropertyInfo == null)
            {
                jsonPropertyInfo = state.Current.JsonClassInfo.CreateRootObject(options);
            }

            Debug.Assert(jsonPropertyInfo != null);

            // A nested object or dictionary so push new frame.
            if (state.Current.CollectionPropertyInitialized)
            {
                state.Push();
                state.Current.JsonClassInfo = jsonPropertyInfo.ElementClassInfo;
                state.Current.InitializeJsonPropertyInfo();

                JsonClassInfo classInfo = state.Current.JsonClassInfo;

                if (state.Current.IsProcessingIDictionaryConstructible())
                {
                    state.Current.TempDictionaryValues          = (IDictionary)classInfo.CreateConcreteDictionary();
                    state.Current.CollectionPropertyInitialized = true;
                }
                else if (state.Current.IsProcessingDictionary())
                {
                    if (classInfo.CreateObject == null)
                    {
                        throw ThrowHelper.GetNotSupportedException_SerializationNotSupportedCollection(classInfo.Type, parentType: null, memberInfo: null);
                    }

                    state.Current.ReturnValue = classInfo.CreateObject();
                    state.Current.CollectionPropertyInitialized = true;
                }
                else if (state.Current.IsProcessingObject(ClassType.Object))
                {
                    if (classInfo.CreateObject == null)
                    {
                        ThrowHelper.ThrowNotSupportedException_DeserializeCreateObjectDelegateIsNull(classInfo.Type);
                    }

                    state.Current.ReturnValue = classInfo.CreateObject();
                }
                else
                {
                    ThrowHelper.ThrowJsonException_DeserializeUnableToConvertValue(classInfo.Type);
                }

                return;
            }

            state.Current.CollectionPropertyInitialized = true;

            if (state.Current.IsProcessingIDictionaryConstructible())
            {
                JsonClassInfo dictionaryClassInfo;
                if (jsonPropertyInfo.DeclaredPropertyType == jsonPropertyInfo.ImplementedPropertyType)
                {
                    dictionaryClassInfo = options.GetOrAddClass(jsonPropertyInfo.RuntimePropertyType);
                }
                else
                {
                    dictionaryClassInfo = options.GetOrAddClass(jsonPropertyInfo.DeclaredPropertyType);
                }

                state.Current.TempDictionaryValues = (IDictionary)dictionaryClassInfo.CreateConcreteDictionary();
            }
            else
            {
                // Create the dictionary.
                JsonClassInfo dictionaryClassInfo = jsonPropertyInfo.RuntimeClassInfo;
                IDictionary   value = (IDictionary)dictionaryClassInfo.CreateObject();

                if (value != null)
                {
                    if (state.Current.ReturnValue != null)
                    {
                        state.Current.JsonPropertyInfo.SetValueAsObject(state.Current.ReturnValue, value);
                    }
                    else
                    {
                        // A dictionary is being returned directly, or a nested dictionary.
                        state.Current.SetReturnValue(value);
                    }
                }
            }
        }
示例#26
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;
            }
        }
        private static bool TryParseValue(ref Utf8JsonReader reader, [NotNullWhen(true)] out JsonDocument?document, bool shouldThrow)
        {
            JsonReaderState state = reader.CurrentState;

            CheckSupportedOptions(state.Options, nameof(reader));

            // Value copy to overwrite the ref on an exception and undo the destructive reads.
            Utf8JsonReader restore = reader;

            ReadOnlySpan <byte>     valueSpan     = default;
            ReadOnlySequence <byte> valueSequence = default;

            try
            {
                switch (reader.TokenType)
                {
                // A new reader was created and has never been read,
                // so we need to move to the first token.
                // (or a reader has terminated and we're about to throw)
                case JsonTokenType.None:
                // Using a reader loop the caller has identified a property they wish to
                // hydrate into a JsonDocument. Move to the value first.
                case JsonTokenType.PropertyName:
                {
                    if (!reader.Read())
                    {
                        if (shouldThrow)
                        {
                            ThrowHelper.ThrowJsonReaderException(
                                ref reader,
                                ExceptionResource.ExpectedJsonTokens);
                        }

                        reader   = restore;
                        document = null;
                        return(false);
                    }
                    break;
                }
                }

                switch (reader.TokenType)
                {
                // Any of the "value start" states are acceptable.
                case JsonTokenType.StartObject:
                case JsonTokenType.StartArray:
                {
                    long startingOffset = reader.TokenStartIndex;

                    if (!reader.TrySkip())
                    {
                        if (shouldThrow)
                        {
                            ThrowHelper.ThrowJsonReaderException(
                                ref reader,
                                ExceptionResource.ExpectedJsonTokens);
                        }

                        reader   = restore;
                        document = null;
                        return(false);
                    }

                    long totalLength = reader.BytesConsumed - startingOffset;
                    ReadOnlySequence <byte> sequence = reader.OriginalSequence;

                    if (sequence.IsEmpty)
                    {
                        valueSpan = reader.OriginalSpan.Slice(
                            checked ((int)startingOffset),
                            checked ((int)totalLength));
                    }
                    else
                    {
                        valueSequence = sequence.Slice(startingOffset, totalLength);
                    }

                    Debug.Assert(
                        reader.TokenType == JsonTokenType.EndObject ||
                        reader.TokenType == JsonTokenType.EndArray);

                    break;
                }

                // Single-token values
                case JsonTokenType.Number:
                case JsonTokenType.True:
                case JsonTokenType.False:
                case JsonTokenType.Null:
                {
                    if (reader.HasValueSequence)
                    {
                        valueSequence = reader.ValueSequence;
                    }
                    else
                    {
                        valueSpan = reader.ValueSpan;
                    }

                    break;
                }

                // String's ValueSequence/ValueSpan omits the quotes, we need them back.
                case JsonTokenType.String:
                {
                    ReadOnlySequence <byte> sequence = reader.OriginalSequence;

                    if (sequence.IsEmpty)
                    {
                        // Since the quoted string fit in a ReadOnlySpan originally
                        // the contents length plus the two quotes can't overflow.
                        int payloadLength = reader.ValueSpan.Length + 2;
                        Debug.Assert(payloadLength > 1);

                        ReadOnlySpan <byte> readerSpan = reader.OriginalSpan;

                        Debug.Assert(
                            readerSpan[(int)reader.TokenStartIndex] == (byte)'"',
                            $"Calculated span starts with {readerSpan[(int)reader.TokenStartIndex]}");

                        Debug.Assert(
                            readerSpan[(int)reader.TokenStartIndex + payloadLength - 1] == (byte)'"',
                            $"Calculated span ends with {readerSpan[(int)reader.TokenStartIndex + payloadLength - 1]}");

                        valueSpan = readerSpan.Slice((int)reader.TokenStartIndex, payloadLength);
                    }
                    else
                    {
                        long payloadLength = 2;

                        if (reader.HasValueSequence)
                        {
                            payloadLength += reader.ValueSequence.Length;
                        }
                        else
                        {
                            payloadLength += reader.ValueSpan.Length;
                        }

                        valueSequence = sequence.Slice(reader.TokenStartIndex, payloadLength);
                        Debug.Assert(
                            valueSequence.First.Span[0] == (byte)'"',
                            $"Calculated sequence starts with {valueSequence.First.Span[0]}");

                        Debug.Assert(
                            valueSequence.ToArray()[payloadLength - 1] == (byte)'"',
                            $"Calculated sequence ends with {valueSequence.ToArray()[payloadLength - 1]}");
                    }

                    break;
                }

                default:
                {
                    if (shouldThrow)
                    {
                        // Default case would only hit if TokenType equals JsonTokenType.EndObject or JsonTokenType.EndArray in which case it would never be sequence
                        Debug.Assert(!reader.HasValueSequence);
                        byte displayByte = reader.ValueSpan[0];

                        ThrowHelper.ThrowJsonReaderException(
                            ref reader,
                            ExceptionResource.ExpectedStartOfValueNotFound,
                            displayByte);
                    }

                    reader   = restore;
                    document = null;
                    return(false);
                }
                }
            }
            catch
            {
                reader = restore;
                throw;
            }

            int length = valueSpan.IsEmpty ? checked ((int)valueSequence.Length) : valueSpan.Length;

            byte[] rented = ArrayPool <byte> .Shared.Rent(length);

            Span <byte> rentedSpan = rented.AsSpan(0, length);

            try
            {
                if (valueSpan.IsEmpty)
                {
                    valueSequence.CopyTo(rentedSpan);
                }
                else
                {
                    valueSpan.CopyTo(rentedSpan);
                }

                document = Parse(rented.AsMemory(0, length), state.Options, rented);
                return(true);
            }
            catch
            {
                // This really shouldn't happen since the document was already checked
                // for consistency by Skip.  But if data mutations happened just after
                // the calls to Read then the copy may not be valid.
                rentedSpan.Clear();
                ArrayPool <byte> .Shared.Return(rented);

                throw;
            }
        }
示例#28
0
        private void DetermineSerializationCapabilities()
        {
            if (ClassType != ClassType.Enumerable &&
                ClassType != ClassType.Dictionary &&
                ClassType != ClassType.IDictionaryConstructible)
            {
                // We serialize if there is a getter + not ignoring readonly properties.
                ShouldSerialize = HasGetter && (HasSetter || !Options.IgnoreReadOnlyProperties);

                // We deserialize if there is a setter.
                ShouldDeserialize = HasSetter;
            }
            else
            {
                if (HasGetter)
                {
                    ShouldSerialize = true;

                    if (HasSetter)
                    {
                        ShouldDeserialize = true;

                        if (RuntimePropertyType.IsArray)
                        {
                            // Verify that we don't have a multidimensional array.
                            if (RuntimePropertyType.GetArrayRank() > 1)
                            {
                                throw ThrowHelper.GetNotSupportedException_SerializationNotSupportedCollection(RuntimePropertyType, ParentClassType, PropertyInfo);
                            }

                            EnumerableConverter = s_jsonArrayConverter;
                        }
                        else if (ClassType == ClassType.IDictionaryConstructible)
                        {
                            // Natively supported type.
                            if (DeclaredPropertyType == ImplementedPropertyType)
                            {
                                if (RuntimePropertyType.FullName.StartsWith(JsonClassInfo.ImmutableNamespaceName))
                                {
                                    DefaultImmutableDictionaryConverter.RegisterImmutableDictionary(
                                        RuntimePropertyType, JsonClassInfo.GetElementType(RuntimePropertyType, ParentClassType, PropertyInfo, Options), Options);

                                    DictionaryConverter = s_jsonImmutableDictionaryConverter;
                                }
                                else if (JsonClassInfo.IsDeserializedByConstructingWithIDictionary(RuntimePropertyType))
                                {
                                    DictionaryConverter = s_jsonIDictionaryConverter;
                                }
                            }
                            // Type that implements a type with ClassType IDictionaryConstructible.
                            else
                            {
                                DictionaryConverter = s_jsonDerivedDictionaryConverter;
                            }
                        }
                        else if (ClassType == ClassType.Enumerable)
                        {
                            // Else if it's an implementing type whose runtime type is not assignable to IList.
                            if (DeclaredPropertyType != ImplementedPropertyType &&
                                (!typeof(IList).IsAssignableFrom(RuntimePropertyType) ||
                                 ImplementedPropertyType == typeof(ArrayList) ||
                                 ImplementedPropertyType == typeof(IList)))
                            {
                                EnumerableConverter = s_jsonDerivedEnumerableConverter;
                            }
                            else if (JsonClassInfo.IsDeserializedByConstructingWithIList(RuntimePropertyType) ||
                                     (!typeof(IList).IsAssignableFrom(RuntimePropertyType) &&
                                      JsonClassInfo.HasConstructorThatTakesGenericIEnumerable(RuntimePropertyType, Options)))
                            {
                                EnumerableConverter = s_jsonICollectionConverter;
                            }
                            else if (RuntimePropertyType.IsGenericType &&
                                     RuntimePropertyType.FullName.StartsWith(JsonClassInfo.ImmutableNamespaceName) &&
                                     RuntimePropertyType.GetGenericArguments().Length == 1)
                            {
                                DefaultImmutableEnumerableConverter.RegisterImmutableCollection(RuntimePropertyType,
                                                                                                JsonClassInfo.GetElementType(RuntimePropertyType, ParentClassType, PropertyInfo, Options), Options);
                                EnumerableConverter = s_jsonImmutableEnumerableConverter;
                            }
                        }
                    }
                }
            }
        }
示例#29
0
 private void WriteStart(byte token)
 {
     if (CurrentDepth >= JsonConstants.MaxWriterDepth)
     {
         ThrowHelper.ThrowInvalidOperationException(ExceptionResource.DepthTooLarge, _currentDepth, token: default, tokenType: default);
示例#30
0
        private static void HandleStartArray(
            JsonSerializerOptions options,
            ref Utf8JsonReader reader,
            ref ReadStack state)
        {
            JsonPropertyInfo jsonPropertyInfo = state.Current.JsonPropertyInfo;

            if (state.Current.SkipProperty)
            {
                // The array is not being applied to the object.
                state.Push();
                state.Current.Drain = true;
                return;
            }

            if (jsonPropertyInfo == null)
            {
                jsonPropertyInfo = state.Current.JsonClassInfo.CreateRootObject(options);
            }
            else if (state.Current.JsonClassInfo.ClassType == ClassType.Unknown)
            {
                jsonPropertyInfo = state.Current.JsonClassInfo.CreatePolymorphicProperty(jsonPropertyInfo, typeof(object), options);
            }

            // Verify that we don't have a multidimensional array.
            Type arrayType = jsonPropertyInfo.RuntimePropertyType;

            if (!typeof(IEnumerable).IsAssignableFrom(arrayType) || (arrayType.IsArray && arrayType.GetArrayRank() > 1))
            {
                ThrowHelper.ThrowJsonException_DeserializeUnableToConvertValue(arrayType, reader, state.JsonPath);
            }

            Debug.Assert(state.Current.IsProcessingEnumerableOrDictionary);

            if (state.Current.PropertyInitialized)
            {
                // A nested json array so push a new stack frame.
                Type elementType = jsonPropertyInfo.ElementClassInfo.Type;

                state.Push();
                state.Current.Initialize(elementType, options);
                state.Current.PropertyInitialized = true;
            }
            else
            {
                state.Current.PropertyInitialized = true;
            }

            jsonPropertyInfo = state.Current.JsonPropertyInfo;

            if (state.Current.JsonClassInfo.ClassType == ClassType.Value)
            {
                state.Current.JsonPropertyInfo.Read(JsonTokenType.StartObject, ref state, ref reader);
            }
            else
            {
                // If current property is already set (from a constructor, for example) leave as-is.
                if (jsonPropertyInfo.GetValueAsObject(state.Current.ReturnValue) == null)
                {
                    // Create the enumerable.
                    object value = ReadStackFrame.CreateEnumerableValue(ref reader, ref state, options);

                    // If value is not null, then we don't have a converter so apply the value.
                    if (value != null)
                    {
                        if (state.Current.ReturnValue != null)
                        {
                            state.Current.JsonPropertyInfo.SetValueAsObject(state.Current.ReturnValue, value);
                        }
                        else
                        {
                            // Primitive arrays being returned without object
                            state.Current.SetReturnValue(value);
                        }
                    }
                }
            }
        }