private static byte[] GetEscapedPropertyNameSection(
            ReadOnlySpan <byte> utf8Value,
            int firstEscapeIndexVal,
            JavaScriptEncoder?encoder)
        {
            Debug.Assert(int.MaxValue / JsonConstants.MaxExpansionFactorWhileEscaping >= utf8Value.Length);
            Debug.Assert(firstEscapeIndexVal >= 0 && firstEscapeIndexVal < utf8Value.Length);

            byte[]? valueArray = null;

            int length = JsonWriterHelper.GetMaxEscapedLength(utf8Value.Length, firstEscapeIndexVal);

            Span <byte> escapedValue = length <= JsonConstants.StackallocByteThreshold ?
                                       stackalloc byte[JsonConstants.StackallocByteThreshold] :
                                       (valueArray = ArrayPool <byte> .Shared.Rent(length));

            JsonWriterHelper.EscapeString(utf8Value, escapedValue, firstEscapeIndexVal, encoder, out int written);

            byte[] propertySection = GetPropertyNameSection(escapedValue.Slice(0, written));

            if (valueArray != null)
            {
                ArrayPool <byte> .Shared.Return(valueArray);
            }

            return(propertySection);
        }
        // todo: https://github.com/dotnet/runtime/issues/32352
        // it is possible to cache the underlying converters since this is an internal converter and
        // an instance is created only once for each JsonSerializerOptions instance.

        internal override void Initialize(JsonSerializerOptions options)
        {
            JsonNamingPolicy?namingPolicy = options.PropertyNamingPolicy;

            if (namingPolicy == null)
            {
                _keyName   = KeyNameCLR;
                _valueName = ValueNameCLR;
            }
            else
            {
                _keyName   = namingPolicy.ConvertName(KeyNameCLR);
                _valueName = namingPolicy.ConvertName(ValueNameCLR);

                if (_keyName == null || _valueName == null)
                {
                    ThrowHelper.ThrowInvalidOperationException_NamingPolicyReturnNull(namingPolicy);
                }
            }

            JavaScriptEncoder?encoder = options.Encoder;

            _keyNameEncoded   = JsonEncodedText.Encode(_keyName, encoder);
            _valueNameEncoded = JsonEncodedText.Encode(_valueName, encoder);
        }
Exemple #3
0
        /// <summary>
        /// Copies the options from a <see cref="JsonSerializerOptions"/> instance to a new instance.
        /// </summary>
        /// <param name="options">The <see cref="JsonSerializerOptions"/> instance to copy options from.</param>
        /// <exception cref="System.ArgumentNullException">
        /// <paramref name="options"/> is <see langword="null"/>.
        /// </exception>
        public JsonSerializerOptions(JsonSerializerOptions options)
        {
            if (options == null)
            {
                throw new ArgumentNullException(nameof(options));
            }

            _memberAccessorStrategy   = options._memberAccessorStrategy;
            _dictionaryKeyPolicy      = options._dictionaryKeyPolicy;
            _jsonPropertyNamingPolicy = options._jsonPropertyNamingPolicy;
            _readCommentHandling      = options._readCommentHandling;
            _referenceHandler         = options._referenceHandler;
            _encoder = options._encoder;
            _defaultIgnoreCondition = options._defaultIgnoreCondition;

            _defaultBufferSize           = options._defaultBufferSize;
            _maxDepth                    = options._maxDepth;
            _allowTrailingCommas         = options._allowTrailingCommas;
            _ignoreNullValues            = options._ignoreNullValues;
            _ignoreReadOnlyProperties    = options._ignoreReadOnlyProperties;
            _ignoreReadonlyFields        = options._ignoreReadonlyFields;
            _includeFields               = options._includeFields;
            _propertyNameCaseInsensitive = options._propertyNameCaseInsensitive;
            _writeIndented               = options._writeIndented;

            Converters        = new ConverterList(this, (ConverterList)options.Converters);
            EffectiveMaxDepth = options.EffectiveMaxDepth;

            // _classes is not copied as sharing the JsonClassInfo and JsonPropertyInfo caches can result in
            // unnecessary references to type metadata, potentially hindering garbage collection on the source options.

            // _haveTypesBeenCreated is not copied; it's okay to make changes to this options instance as (de)serialization has not occurred.
        }
        public EnumConverter(EnumConverterOptions converterOptions, JsonNamingPolicy?namingPolicy, JsonSerializerOptions serializerOptions)
        {
            _converterOptions = converterOptions;
            _namingPolicy     = namingPolicy;
            _nameCache        = new ConcurrentDictionary <ulong, JsonEncodedText>();

            string[] names  = Enum.GetNames(TypeToConvert);
            Array    values = Enum.GetValues(TypeToConvert);

            Debug.Assert(names.Length == values.Length);

            JavaScriptEncoder?encoder = serializerOptions.Encoder;

            for (int i = 0; i < names.Length; i++)
            {
                if (_nameCache.Count >= NameCacheSizeSoftLimit)
                {
                    break;
                }

                T      value = (T)values.GetValue(i) !;
                ulong  key   = ConvertToUInt64(value);
                string name  = names[i];

                _nameCache.TryAdd(
                    key,
                    namingPolicy == null
                        ? JsonEncodedText.Encode(name, encoder)
                        : FormatEnumValue(name, encoder));
            }
        }
Exemple #5
0
        /// <summary>
        /// Encodes the string text value as a JSON string.
        /// </summary>
        /// <param name="value">The value to be transformed as JSON encoded text.</param>
        /// <param name="encoder">The encoder to use when escaping the string, or <see langword="null" /> to use the default encoder.</param>
        /// <exception cref="ArgumentNullException">
        /// Thrown if value is null.
        /// </exception>
        /// <exception cref="ArgumentException">
        /// Thrown when the specified value is too large or if it contains invalid UTF-16 characters.
        /// </exception>
        public static JsonEncodedText Encode(string value, JavaScriptEncoder?encoder = null)
        {
            if (value is null)
            {
                ThrowHelper.ThrowArgumentNullException(nameof(value));
            }

            return(Encode(value.AsSpan(), encoder));
        }
Exemple #6
0
        /// <summary>
        /// Encodes the text value as a JSON string.
        /// </summary>
        /// <param name="value">The value to be transformed as JSON encoded text.</param>
        /// <param name="encoder">The encoder to use when escaping the string, or <see langword="null" /> to use the default encoder.</param>
        /// <exception cref="ArgumentException">
        /// Thrown when the specified value is too large or if it contains invalid UTF-16 characters.
        /// </exception>
        public static JsonEncodedText Encode(ReadOnlySpan <char> value, JavaScriptEncoder?encoder = null)
        {
            if (value.Length == 0)
            {
                return(new JsonEncodedText(Array.Empty <byte>()));
            }

            return(TranscodeAndEncode(value, encoder));
        }
Exemple #7
0
        /// <summary>
        /// Encodes the UTF-8 text value as a JSON string.
        /// </summary>
        /// <param name="utf8Value">The UTF-8 encoded value to be transformed as JSON encoded text.</param>
        /// <param name="encoder">The encoder to use when escaping the string, or <see langword="null" /> to use the default encoder.</param>
        /// <exception cref="ArgumentException">
        /// Thrown when the specified value is too large or if it contains invalid UTF-8 bytes.
        /// </exception>
        public static JsonEncodedText Encode(ReadOnlySpan <byte> utf8Value, JavaScriptEncoder?encoder = null)
        {
            if (utf8Value.Length == 0)
            {
                return(new JsonEncodedText(Array.Empty <byte>()));
            }

            JsonWriterHelper.ValidateValue(utf8Value);
            return(EncodeHelper(utf8Value, encoder));
        }
Exemple #8
0
        /// <summary>
        /// Internal version that keeps the existing string and byte[] references if there is no escaping required.
        /// </summary>
        internal static JsonEncodedText Encode(string stringValue, byte[] utf8Value, JavaScriptEncoder?encoder = null)
        {
            Debug.Assert(stringValue.Equals(JsonHelpers.Utf8GetString(utf8Value)));

            if (utf8Value.Length == 0)
            {
                return(new JsonEncodedText(stringValue, utf8Value));
            }

            JsonWriterHelper.ValidateValue(utf8Value);
            return(EncodeHelper(stringValue, utf8Value, encoder));
        }
Exemple #9
0
        private static JsonEncodedText EncodeHelper(ReadOnlySpan <byte> utf8Value, JavaScriptEncoder?encoder)
        {
            int idx = JsonWriterHelper.NeedsEscaping(utf8Value, encoder);

            if (idx != -1)
            {
                return(new JsonEncodedText(JsonHelpers.EscapeValue(utf8Value, idx, encoder)));
            }
            else
            {
                return(new JsonEncodedText(utf8Value.ToArray()));
            }
        }
        public static unsafe int NeedsEscaping(ReadOnlySpan <char> value, JavaScriptEncoder?encoder)
        {
            // Some implementations of JavaScriptEncoder.FindFirstCharacterToEncode may not accept
            // null pointers and guard against that. Hence, check up-front to return -1.
            if (value.IsEmpty)
            {
                return(-1);
            }

            fixed(char *ptr = value)
            {
                return((encoder ?? JavaScriptEncoder.Default).FindFirstCharacterToEncode(ptr, value.Length));
            }
        }
Exemple #11
0
        /// <summary>
        /// Copies the options from a <see cref="JsonSerializerOptions"/> instance to a new instance.
        /// </summary>
        /// <param name="options">The <see cref="JsonSerializerOptions"/> instance to copy options from.</param>
        /// <exception cref="System.ArgumentNullException">
        /// <paramref name="options"/> is <see langword="null"/>.
        /// </exception>
        public JsonSerializerOptions(JsonSerializerOptions options)
        {
            if (options is null)
            {
                ThrowHelper.ThrowArgumentNullException(nameof(options));
            }

            _memberAccessorStrategy   = options._memberAccessorStrategy;
            _dictionaryKeyPolicy      = options._dictionaryKeyPolicy;
            _jsonPropertyNamingPolicy = options._jsonPropertyNamingPolicy;
            _readCommentHandling      = options._readCommentHandling;
            _referenceHandler         = options._referenceHandler;
            _converters             = new ConverterList(this, options._converters);
            _encoder                = options._encoder;
            _defaultIgnoreCondition = options._defaultIgnoreCondition;
            _numberHandling         = options._numberHandling;
            _unknownTypeHandling    = options._unknownTypeHandling;

            _defaultBufferSize           = options._defaultBufferSize;
            _maxDepth                    = options._maxDepth;
            _allowTrailingCommas         = options._allowTrailingCommas;
            _ignoreNullValues            = options._ignoreNullValues;
            _ignoreReadOnlyProperties    = options._ignoreReadOnlyProperties;
            _ignoreReadonlyFields        = options._ignoreReadonlyFields;
            _includeFields               = options._includeFields;
            _propertyNameCaseInsensitive = options._propertyNameCaseInsensitive;
            _writeIndented               = options._writeIndented;
            // Preserve backward compatibility with .NET 6
            // This should almost certainly be changed, cf. https://github.com/dotnet/aspnetcore/issues/38720
            _typeInfoResolver         = options._typeInfoResolver is JsonSerializerContext ? null : options._typeInfoResolver;
            EffectiveMaxDepth         = options.EffectiveMaxDepth;
            ReferenceHandlingStrategy = options.ReferenceHandlingStrategy;

            // _cachingContext is not copied as sharing the JsonTypeInfo and JsonPropertyInfo caches can result in
            // unnecessary references to type metadata, potentially hindering garbage collection on the source options.

            TrackOptionsInstance(this);
        }
Exemple #12
0
        public EnumConverter(EnumConverterOptions converterOptions, JsonNamingPolicy?namingPolicy, JsonSerializerOptions serializerOptions)
        {
            _converterOptions    = converterOptions;
            _namingPolicy        = namingPolicy;
            _nameCacheForWriting = new ConcurrentDictionary <ulong, JsonEncodedText>();

            if (namingPolicy != null)
            {
                _nameCacheForReading = new ConcurrentDictionary <string, T>();
            }

#if NETCOREAPP
            string[] names  = Enum.GetNames <T>();
            T[]      values = Enum.GetValues <T>();
#else
            string[] names  = Enum.GetNames(TypeToConvert);
            Array    values = Enum.GetValues(TypeToConvert);
#endif
            Debug.Assert(names.Length == values.Length);

            JavaScriptEncoder?encoder = serializerOptions.Encoder;

            for (int i = 0; i < names.Length; i++)
            {
#if NETCOREAPP
                T value = values[i];
#else
                T value = (T)values.GetValue(i) !;
#endif
                ulong  key  = ConvertToUInt64(value);
                string name = names[i];

                string jsonName = FormatJsonName(name, namingPolicy);
                _nameCacheForWriting.TryAdd(key, JsonEncodedText.Encode(jsonName, encoder));
                _nameCacheForReading?.TryAdd(jsonName, value);
            }
        }
Exemple #13
0
        private static JsonEncodedText TranscodeAndEncode(ReadOnlySpan <char> value, JavaScriptEncoder?encoder)
        {
            JsonWriterHelper.ValidateValue(value);

            int expectedByteCount = JsonReaderHelper.GetUtf8ByteCount(value);

            byte[] utf8Bytes = ArrayPool <byte> .Shared.Rent(expectedByteCount);

            JsonEncodedText encodedText;

            // Since GetUtf8ByteCount above already throws on invalid input, the transcoding
            // to UTF-8 is guaranteed to succeed here. Therefore, there's no need for a try-catch-finally block.
            int actualByteCount = JsonReaderHelper.GetUtf8FromText(value, utf8Bytes);

            Debug.Assert(expectedByteCount == actualByteCount);

            encodedText = EncodeHelper(utf8Bytes.AsSpan(0, actualByteCount), encoder);

            // On the basis that this is user data, go ahead and clear it.
            utf8Bytes.AsSpan(0, expectedByteCount).Clear();
            ArrayPool <byte> .Shared.Return(utf8Bytes);

            return(encodedText);
        }
        public static byte[] GetEscapedPropertyNameSection(ReadOnlySpan <byte> utf8Value, JavaScriptEncoder?encoder)
        {
            int idx = JsonWriterHelper.NeedsEscaping(utf8Value, encoder);

            if (idx != -1)
            {
                return(GetEscapedPropertyNameSection(utf8Value, idx, encoder));
            }
            else
            {
                return(GetPropertyNameSection(utf8Value));
            }
        }
Exemple #15
0
        public NamingPolicyRespectJsonStringEnumConverter(JsonNamingPolicy?namingPolicy, JavaScriptEncoder?encoder, bool serializeAsStrings)
        {
            this.serializeAsStrings = serializeAsStrings;
            namingPolicy ??= new AsIsNamingPolicy();

            var names  = Enum.GetNames(TypeToConvert);
            var values = Enum.GetValues(TypeToConvert);

            Debug.Assert(names.Length == values.Length);

            valuesToNames   = new Dictionary <T, JsonEncodedText>(names.Length);
            stringsToValues = new Dictionary <string, T>(names.Length);
            for (var i = 0; i < names.Length; i++)
            {
                var value = (T)values.GetValue(i) !;
                var name  = namingPolicy.ConvertName(names[i]);
                valuesToNames.Add(value, JsonEncodedText.Encode(name, encoder));
                stringsToValues.Add(name, value);
            }
        }
        public static void EscapeString(ReadOnlySpan <char> value, Span <char> destination, int indexOfFirstByteToEscape, JavaScriptEncoder?encoder, out int written)
        {
            Debug.Assert(indexOfFirstByteToEscape >= 0 && indexOfFirstByteToEscape < value.Length);

            value.Slice(0, indexOfFirstByteToEscape).CopyTo(destination);
            written = indexOfFirstByteToEscape;

            if (encoder != null)
            {
                destination = destination.Slice(indexOfFirstByteToEscape);
                value       = value.Slice(indexOfFirstByteToEscape);
                EscapeString(value, destination, encoder, ref written);
            }
            else
            {
                // For performance when no encoder is specified, perform escaping here for Ascii and on the
                // first occurrence of a non-Ascii character, then call into the default encoder.
                while (indexOfFirstByteToEscape < value.Length)
                {
                    char val = value[indexOfFirstByteToEscape];
                    if (IsAsciiValue(val))
                    {
                        if (NeedsEscapingNoBoundsCheck(val))
                        {
                            EscapeNextChars(val, destination, ref written);
                            indexOfFirstByteToEscape++;
                        }
                        else
                        {
                            destination[written] = val;
                            written++;
                            indexOfFirstByteToEscape++;
                        }
                    }
                    else
                    {
                        // Fall back to default encoder.
                        destination = destination.Slice(written);
                        value       = value.Slice(indexOfFirstByteToEscape);
                        EscapeString(value, destination, JavaScriptEncoder.Default, ref written);
                        break;
                    }
                }
            }
        }
Exemple #17
0
        private static JsonEncodedText EncodeHelper(string stringValue, byte[] utf8Value, JavaScriptEncoder?encoder)
        {
            int idx = JsonWriterHelper.NeedsEscaping(utf8Value, encoder);

            if (idx != -1)
            {
                return(new JsonEncodedText(GetEscapedString(utf8Value, idx, encoder)));
            }
            else
            {
                // Encoding is not necessary; use the same stringValue and utf8Value references.
                return(new JsonEncodedText(stringValue, utf8Value));
            }
        }
 public static int NeedsEscaping(ReadOnlySpan <byte> value, JavaScriptEncoder?encoder)
 {
     return((encoder ?? JavaScriptEncoder.Default).FindFirstCharacterToEncodeUtf8(value));
 }