Skip to content
Permalink
release/3.0
Switch branches/tags
Go to file
 
 
Cannot retrieve contributors at this time
// 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();
}
}
}
}