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); }
/// <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)); } }
/// <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)); }
/// <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)); }
/// <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)); }
/// <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)); }
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)); } }
/// <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); }
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); } }
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)); } }
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; } } } }
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)); }