Permalink
Cannot retrieve contributors at this time
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
399 lines (360 sloc)
14.1 KB
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// Licensed to the .NET Foundation under one or more agreements. | |
// The .NET Foundation licenses this file to you under the MIT license. | |
// See the LICENSE file in the project root for more information. | |
using System.Collections.Generic; | |
using System.Collections.Concurrent; | |
using System.Diagnostics; | |
using System.Text.Json.Serialization; | |
using System.Text.Encodings.Web; | |
namespace System.Text.Json | |
{ | |
/// <summary> | |
/// Provides options to be used with <see cref="JsonSerializer"/>. | |
/// </summary> | |
public sealed partial class JsonSerializerOptions | |
{ | |
internal const int BufferSizeDefault = 16 * 1024; | |
internal static readonly JsonSerializerOptions s_defaultOptions = new JsonSerializerOptions(); | |
private readonly ConcurrentDictionary<Type, JsonClassInfo> _classes = new ConcurrentDictionary<Type, JsonClassInfo>(); | |
private readonly ConcurrentDictionary<Type, JsonPropertyInfo> _objectJsonProperties = new ConcurrentDictionary<Type, JsonPropertyInfo>(); | |
private static ConcurrentDictionary<string, ImmutableCollectionCreator> s_createRangeDelegates = new ConcurrentDictionary<string, ImmutableCollectionCreator>(); | |
private MemberAccessor _memberAccessorStrategy; | |
private JsonNamingPolicy _dictionayKeyPolicy; | |
private JsonNamingPolicy _jsonPropertyNamingPolicy; | |
private JsonCommentHandling _readCommentHandling; | |
private JavaScriptEncoder _encoder; | |
private int _defaultBufferSize = BufferSizeDefault; | |
private int _maxDepth; | |
private bool _allowTrailingCommas; | |
private bool _haveTypesBeenCreated; | |
private bool _ignoreNullValues; | |
private bool _ignoreReadOnlyProperties; | |
private bool _propertyNameCaseInsensitive; | |
private bool _writeIndented; | |
/// <summary> | |
/// Constructs a new <see cref="JsonSerializerOptions"/> instance. | |
/// </summary> | |
public JsonSerializerOptions() | |
{ | |
Converters = new ConverterList(this); | |
} | |
/// <summary> | |
/// Defines whether an extra comma at the end of a list of JSON values in an object or array | |
/// is allowed (and ignored) within the JSON payload being deserialized. | |
/// </summary> | |
/// <exception cref="InvalidOperationException"> | |
/// Thrown if this property is set after serialization or deserialization has occurred. | |
/// </exception> | |
/// <remarks> | |
/// By default, it's set to false, and <exception cref="JsonException"/> is thrown if a trailing comma is encountered. | |
/// </remarks> | |
public bool AllowTrailingCommas | |
{ | |
get | |
{ | |
return _allowTrailingCommas; | |
} | |
set | |
{ | |
VerifyMutable(); | |
_allowTrailingCommas = value; | |
} | |
} | |
/// <summary> | |
/// The default buffer size in bytes used when creating temporary buffers. | |
/// </summary> | |
/// <remarks>The default size is 16K.</remarks> | |
/// <exception cref="System.ArgumentException">Thrown when the buffer size is less than 1.</exception> | |
/// <exception cref="InvalidOperationException"> | |
/// Thrown if this property is set after serialization or deserialization has occurred. | |
/// </exception> | |
public int DefaultBufferSize | |
{ | |
get | |
{ | |
return _defaultBufferSize; | |
} | |
set | |
{ | |
VerifyMutable(); | |
if (value < 1) | |
{ | |
throw new ArgumentException(SR.SerializationInvalidBufferSize); | |
} | |
_defaultBufferSize = value; | |
} | |
} | |
/// <summary> | |
/// The encoder to use when escaping strings, or <see langword="null" /> to use the default encoder. | |
/// </summary> | |
public JavaScriptEncoder Encoder | |
{ | |
get | |
{ | |
return _encoder; | |
} | |
set | |
{ | |
VerifyMutable(); | |
_encoder = value; | |
} | |
} | |
/// <summary> | |
/// Specifies the policy used to convert a <see cref="System.Collections.IDictionary"/> key's name to another format, such as camel-casing. | |
/// </summary> | |
/// <remarks> | |
/// This property can be set to <see cref="JsonNamingPolicy.CamelCase"/> to specify a camel-casing policy. | |
/// It is not used when deserializing. | |
/// </remarks> | |
public JsonNamingPolicy DictionaryKeyPolicy | |
{ | |
get | |
{ | |
return _dictionayKeyPolicy; | |
} | |
set | |
{ | |
VerifyMutable(); | |
_dictionayKeyPolicy = value; | |
} | |
} | |
/// <summary> | |
/// Determines whether null values are ignored during serialization and deserialization. | |
/// The default value is false. | |
/// </summary> | |
/// <exception cref="InvalidOperationException"> | |
/// Thrown if this property is set after serialization or deserialization has occurred. | |
/// </exception> | |
public bool IgnoreNullValues | |
{ | |
get | |
{ | |
return _ignoreNullValues; | |
} | |
set | |
{ | |
VerifyMutable(); | |
_ignoreNullValues = value; | |
} | |
} | |
/// <summary> | |
/// Determines whether read-only properties are ignored during serialization. | |
/// A property is read-only if it contains a public getter but not a public setter. | |
/// The default value is false. | |
/// </summary> | |
/// <remarks> | |
/// Read-only properties are not deserialized regardless of this setting. | |
/// </remarks> | |
/// <exception cref="InvalidOperationException"> | |
/// Thrown if this property is set after serialization or deserialization has occurred. | |
/// </exception> | |
public bool IgnoreReadOnlyProperties | |
{ | |
get | |
{ | |
return _ignoreReadOnlyProperties; | |
} | |
set | |
{ | |
VerifyMutable(); | |
_ignoreReadOnlyProperties = value; | |
} | |
} | |
/// <summary> | |
/// Gets or sets the maximum depth allowed when serializing or deserializing JSON, with the default (i.e. 0) indicating a max depth of 64. | |
/// </summary> | |
/// <exception cref="InvalidOperationException"> | |
/// Thrown if this property is set after serialization or deserialization has occurred. | |
/// </exception> | |
/// <exception cref="ArgumentOutOfRangeException"> | |
/// Thrown when the max depth is set to a negative value. | |
/// </exception> | |
/// <remarks> | |
/// Going past this depth will throw a <exception cref="JsonException"/>. | |
/// </remarks> | |
public int MaxDepth | |
{ | |
get => _maxDepth; | |
set | |
{ | |
VerifyMutable(); | |
if (value < 0) | |
{ | |
throw ThrowHelper.GetArgumentOutOfRangeException_MaxDepthMustBePositive(nameof(value)); | |
} | |
_maxDepth = value; | |
EffectiveMaxDepth = (value == 0 ? JsonReaderOptions.DefaultMaxDepth : value); | |
} | |
} | |
// The default is 64 because that is what the reader uses, so re-use the same JsonReaderOptions.DefaultMaxDepth constant. | |
internal int EffectiveMaxDepth { get; private set; } = JsonReaderOptions.DefaultMaxDepth; | |
/// <summary> | |
/// Specifies the policy used to convert a property's name on an object to another format, such as camel-casing. | |
/// The resulting property name is expected to match the JSON payload during deserialization, and | |
/// will be used when writing the property name during serialization. | |
/// </summary> | |
/// <remarks> | |
/// The policy is not used for properties that have a <see cref="JsonPropertyNameAttribute"/> applied. | |
/// This property can be set to <see cref="JsonNamingPolicy.CamelCase"/> to specify a camel-casing policy. | |
/// </remarks> | |
public JsonNamingPolicy PropertyNamingPolicy | |
{ | |
get | |
{ | |
return _jsonPropertyNamingPolicy; | |
} | |
set | |
{ | |
VerifyMutable(); | |
_jsonPropertyNamingPolicy = value; | |
} | |
} | |
/// <summary> | |
/// Determines whether a property's name uses a case-insensitive comparison during deserialization. | |
/// The default value is false. | |
/// </summary> | |
/// <remarks>There is a performance cost associated when the value is true.</remarks> | |
public bool PropertyNameCaseInsensitive | |
{ | |
get | |
{ | |
return _propertyNameCaseInsensitive; | |
} | |
set | |
{ | |
VerifyMutable(); | |
_propertyNameCaseInsensitive = value; | |
} | |
} | |
/// <summary> | |
/// Defines how the comments are handled during deserialization. | |
/// </summary> | |
/// <exception cref="InvalidOperationException"> | |
/// Thrown if this property is set after serialization or deserialization has occurred. | |
/// </exception> | |
/// <exception cref="ArgumentOutOfRangeException"> | |
/// Thrown when the comment handling enum is set to a value that is not supported (or not within the <see cref="JsonCommentHandling"/> enum range). | |
/// </exception> | |
/// <remarks> | |
/// By default <exception cref="JsonException"/> is thrown if a comment is encountered. | |
/// </remarks> | |
public JsonCommentHandling ReadCommentHandling | |
{ | |
get | |
{ | |
return _readCommentHandling; | |
} | |
set | |
{ | |
VerifyMutable(); | |
Debug.Assert(value >= 0); | |
if (value > JsonCommentHandling.Skip) | |
throw new ArgumentOutOfRangeException(nameof(value), SR.JsonSerializerDoesNotSupportComments); | |
_readCommentHandling = value; | |
} | |
} | |
/// <summary> | |
/// Defines whether JSON should pretty print which includes: | |
/// indenting nested JSON tokens, adding new lines, and adding white space between property names and values. | |
/// By default, the JSON is serialized without any extra white space. | |
/// </summary> | |
/// <exception cref="InvalidOperationException"> | |
/// Thrown if this property is set after serialization or deserialization has occurred. | |
/// </exception> | |
public bool WriteIndented | |
{ | |
get | |
{ | |
return _writeIndented; | |
} | |
set | |
{ | |
VerifyMutable(); | |
_writeIndented = value; | |
} | |
} | |
internal MemberAccessor MemberAccessorStrategy | |
{ | |
get | |
{ | |
if (_memberAccessorStrategy == null) | |
{ | |
#if BUILDING_INBOX_LIBRARY | |
_memberAccessorStrategy = new ReflectionEmitMemberAccessor(); | |
#else | |
// todo: should we attempt to detect here, or at least have a #define like #SUPPORTS_IL_EMIT | |
_memberAccessorStrategy = new ReflectionMemberAccessor(); | |
#endif | |
} | |
return _memberAccessorStrategy; | |
} | |
} | |
internal JsonClassInfo GetOrAddClass(Type classType) | |
{ | |
_haveTypesBeenCreated = true; | |
// todo: for performance and reduced instances, consider using the converters and JsonClassInfo from s_defaultOptions by cloning (or reference directly if no changes). | |
if (!_classes.TryGetValue(classType, out JsonClassInfo result)) | |
{ | |
result = _classes.GetOrAdd(classType, new JsonClassInfo(classType, this)); | |
} | |
return result; | |
} | |
internal JsonReaderOptions GetReaderOptions() | |
{ | |
return new JsonReaderOptions | |
{ | |
AllowTrailingCommas = AllowTrailingCommas, | |
CommentHandling = ReadCommentHandling, | |
MaxDepth = MaxDepth | |
}; | |
} | |
internal JsonWriterOptions GetWriterOptions() | |
{ | |
return new JsonWriterOptions | |
{ | |
Encoder = Encoder, | |
Indented = WriteIndented, | |
#if !DEBUG | |
SkipValidation = true | |
#endif | |
}; | |
} | |
internal JsonPropertyInfo GetJsonPropertyInfoFromClassInfo(Type objectType, JsonSerializerOptions options) | |
{ | |
if (!_objectJsonProperties.TryGetValue(objectType, out JsonPropertyInfo propertyInfo)) | |
{ | |
propertyInfo = JsonClassInfo.CreateProperty( | |
objectType, | |
objectType, | |
objectType, | |
propertyInfo: null, | |
typeof(object), | |
converter: null, | |
options); | |
_objectJsonProperties[objectType] = propertyInfo; | |
} | |
return propertyInfo; | |
} | |
internal bool CreateRangeDelegatesContainsKey(string key) | |
{ | |
return s_createRangeDelegates.ContainsKey(key); | |
} | |
internal bool TryGetCreateRangeDelegate(string delegateKey, out ImmutableCollectionCreator createRangeDelegate) | |
{ | |
return s_createRangeDelegates.TryGetValue(delegateKey, out createRangeDelegate) && createRangeDelegate != null; | |
} | |
internal bool TryAddCreateRangeDelegate(string key, ImmutableCollectionCreator createRangeDelegate) | |
{ | |
return s_createRangeDelegates.TryAdd(key, createRangeDelegate); | |
} | |
internal void VerifyMutable() | |
{ | |
// The default options are hidden and thus should be immutable. | |
Debug.Assert(this != s_defaultOptions); | |
if (_haveTypesBeenCreated) | |
{ | |
ThrowHelper.ThrowInvalidOperationException_SerializerOptionsImmutable(); | |
} | |
} | |
} | |
} |