| | | 1 | | // Licensed to the .NET Foundation under one or more agreements. |
| | | 2 | | // The .NET Foundation licenses this file to you under the MIT license. |
| | | 3 | | |
| | | 4 | | using System.Collections.Generic; |
| | | 5 | | using System.ComponentModel; |
| | | 6 | | using System.Diagnostics; |
| | | 7 | | using System.Diagnostics.CodeAnalysis; |
| | | 8 | | using System.Runtime.CompilerServices; |
| | | 9 | | using System.Text.Encodings.Web; |
| | | 10 | | using System.Text.Json.Nodes; |
| | | 11 | | using System.Text.Json.Serialization; |
| | | 12 | | using System.Text.Json.Serialization.Converters; |
| | | 13 | | using System.Text.Json.Serialization.Metadata; |
| | | 14 | | using System.Threading; |
| | | 15 | | |
| | | 16 | | namespace System.Text.Json |
| | | 17 | | { |
| | | 18 | | /// <summary> |
| | | 19 | | /// Provides options to be used with <see cref="JsonSerializer"/>. |
| | | 20 | | /// </summary> |
| | | 21 | | [DebuggerDisplay("{DebuggerDisplay,nq}")] |
| | | 22 | | public sealed partial class JsonSerializerOptions |
| | | 23 | | { |
| | | 24 | | internal const int BufferSizeDefault = 16 * 1024; |
| | | 25 | | |
| | | 26 | | // For backward compatibility the default max depth for JsonSerializer is 64, |
| | | 27 | | // the minimum of JsonReaderOptions.DefaultMaxDepth and JsonWriterOptions.DefaultMaxDepth. |
| | | 28 | | internal const int DefaultMaxDepth = JsonReaderOptions.DefaultMaxDepth; |
| | | 29 | | |
| | | 30 | | /// <summary> |
| | | 31 | | /// Gets a read-only, singleton instance of <see cref="JsonSerializerOptions" /> that uses the default configura |
| | | 32 | | /// </summary> |
| | | 33 | | /// <remarks> |
| | | 34 | | /// Each <see cref="JsonSerializerOptions" /> instance encapsulates its own serialization metadata caches, |
| | | 35 | | /// so using fresh default instances every time one is needed can result in redundant recomputation of converter |
| | | 36 | | /// This property provides a shared instance that can be consumed by any number of components without necessitat |
| | | 37 | | /// </remarks> |
| | | 38 | | public static JsonSerializerOptions Default |
| | | 39 | | { |
| | | 40 | | [RequiresUnreferencedCode(JsonSerializer.SerializationUnreferencedCodeMessage)] |
| | | 41 | | [RequiresDynamicCode(JsonSerializer.SerializationRequiresDynamicCodeMessage)] |
| | 0 | 42 | | get => field ?? GetOrCreateSingleton(ref field, JsonSerializerDefaults.General); |
| | | 43 | | } |
| | | 44 | | |
| | | 45 | | /// <summary> |
| | | 46 | | /// Gets a read-only, singleton instance of <see cref="JsonSerializerOptions" /> that uses the web configuration |
| | | 47 | | /// </summary> |
| | | 48 | | /// <remarks> |
| | | 49 | | /// Each <see cref="JsonSerializerOptions" /> instance encapsulates its own serialization metadata caches, |
| | | 50 | | /// so using fresh default instances every time one is needed can result in redundant recomputation of converter |
| | | 51 | | /// This property provides a shared instance that can be consumed by any number of components without necessitat |
| | | 52 | | /// </remarks> |
| | | 53 | | public static JsonSerializerOptions Web |
| | | 54 | | { |
| | | 55 | | [RequiresUnreferencedCode(JsonSerializer.SerializationUnreferencedCodeMessage)] |
| | | 56 | | [RequiresDynamicCode(JsonSerializer.SerializationRequiresDynamicCodeMessage)] |
| | 0 | 57 | | get => field ?? GetOrCreateSingleton(ref field, JsonSerializerDefaults.Web); |
| | | 58 | | } |
| | | 59 | | |
| | | 60 | | /// <summary> |
| | | 61 | | /// Gets a read-only, singleton instance of <see cref="JsonSerializerOptions" /> that uses the strict configurat |
| | | 62 | | /// </summary> |
| | | 63 | | /// <remarks> |
| | | 64 | | /// Each <see cref="JsonSerializerOptions" /> instance encapsulates its own serialization metadata caches, |
| | | 65 | | /// so using fresh default instances every time one is needed can result in redundant recomputation of converter |
| | | 66 | | /// This property provides a shared instance that can be consumed by any number of components without necessitat |
| | | 67 | | /// </remarks> |
| | | 68 | | public static JsonSerializerOptions Strict |
| | | 69 | | { |
| | | 70 | | [RequiresUnreferencedCode(JsonSerializer.SerializationUnreferencedCodeMessage)] |
| | | 71 | | [RequiresDynamicCode(JsonSerializer.SerializationRequiresDynamicCodeMessage)] |
| | 0 | 72 | | get => field ?? GetOrCreateSingleton(ref field, JsonSerializerDefaults.Strict); |
| | | 73 | | } |
| | | 74 | | |
| | | 75 | | // For any new option added, consider adding it to the options copied in the copy constructor below |
| | | 76 | | // and consider updating the EqualtyComparer used for comparing CachingContexts. |
| | | 77 | | private IJsonTypeInfoResolver? _typeInfoResolver; |
| | | 78 | | private JsonNamingPolicy? _dictionaryKeyPolicy; |
| | | 79 | | private JsonNamingPolicy? _jsonPropertyNamingPolicy; |
| | | 80 | | private JsonCommentHandling _readCommentHandling; |
| | | 81 | | private ReferenceHandler? _referenceHandler; |
| | | 82 | | private JavaScriptEncoder? _encoder; |
| | | 83 | | private ConverterList? _converters; |
| | | 84 | | private JsonIgnoreCondition _defaultIgnoreCondition; |
| | | 85 | | private JsonNumberHandling _numberHandling; |
| | | 86 | | private JsonObjectCreationHandling _preferredObjectCreationHandling; |
| | | 87 | | private JsonUnknownTypeHandling _unknownTypeHandling; |
| | | 88 | | private JsonUnmappedMemberHandling _unmappedMemberHandling; |
| | | 89 | | |
| | 682 | 90 | | private int _defaultBufferSize = BufferSizeDefault; |
| | | 91 | | private int _maxDepth; |
| | | 92 | | private bool _allowOutOfOrderMetadataProperties; |
| | | 93 | | private bool _allowTrailingCommas; |
| | 682 | 94 | | private bool _respectNullableAnnotations = AppContextSwitchHelper.RespectNullableAnnotationsDefault; |
| | 682 | 95 | | private bool _respectRequiredConstructorParameters = AppContextSwitchHelper.RespectRequiredConstructorParameters |
| | | 96 | | private bool _ignoreNullValues; |
| | | 97 | | private bool _ignoreReadOnlyProperties; |
| | | 98 | | private bool _ignoreReadonlyFields; |
| | | 99 | | private bool _includeFields; |
| | | 100 | | private string? _newLine; |
| | | 101 | | private bool _propertyNameCaseInsensitive; |
| | | 102 | | private bool _writeIndented; |
| | 682 | 103 | | private char _indentCharacter = JsonConstants.DefaultIndentCharacter; |
| | 682 | 104 | | private int _indentSize = JsonConstants.DefaultIndentSize; |
| | 682 | 105 | | private bool _allowDuplicateProperties = true; |
| | | 106 | | |
| | | 107 | | /// <summary> |
| | | 108 | | /// Constructs a new <see cref="JsonSerializerOptions"/> instance. |
| | | 109 | | /// </summary> |
| | 682 | 110 | | public JsonSerializerOptions() |
| | 682 | 111 | | { |
| | 682 | 112 | | TrackOptionsInstance(this); |
| | 682 | 113 | | } |
| | | 114 | | |
| | | 115 | | /// <summary> |
| | | 116 | | /// Copies the options from a <see cref="JsonSerializerOptions"/> instance to a new instance. |
| | | 117 | | /// </summary> |
| | | 118 | | /// <param name="options">The <see cref="JsonSerializerOptions"/> instance to copy options from.</param> |
| | | 119 | | /// <exception cref="System.ArgumentNullException"> |
| | | 120 | | /// <paramref name="options"/> is <see langword="null"/>. |
| | | 121 | | /// </exception> |
| | 0 | 122 | | public JsonSerializerOptions(JsonSerializerOptions options) |
| | 0 | 123 | | { |
| | 0 | 124 | | ArgumentNullException.ThrowIfNull(options); |
| | | 125 | | |
| | | 126 | | // The following fields are not copied intentionally: |
| | | 127 | | // 1. _cachingContext can only be set in immutable options instances. |
| | | 128 | | // 2. _typeInfoResolverChain can be created lazily as it relies on |
| | | 129 | | // _typeInfoResolver as its source of truth. |
| | | 130 | | |
| | 0 | 131 | | _dictionaryKeyPolicy = options._dictionaryKeyPolicy; |
| | 0 | 132 | | _jsonPropertyNamingPolicy = options._jsonPropertyNamingPolicy; |
| | 0 | 133 | | _readCommentHandling = options._readCommentHandling; |
| | 0 | 134 | | _referenceHandler = options._referenceHandler; |
| | 0 | 135 | | _converters = options._converters is { } converters ? new(this, converters) : null; |
| | 0 | 136 | | _encoder = options._encoder; |
| | 0 | 137 | | _defaultIgnoreCondition = options._defaultIgnoreCondition; |
| | 0 | 138 | | _numberHandling = options._numberHandling; |
| | 0 | 139 | | _preferredObjectCreationHandling = options._preferredObjectCreationHandling; |
| | 0 | 140 | | _unknownTypeHandling = options._unknownTypeHandling; |
| | 0 | 141 | | _unmappedMemberHandling = options._unmappedMemberHandling; |
| | | 142 | | |
| | 0 | 143 | | _defaultBufferSize = options._defaultBufferSize; |
| | 0 | 144 | | _maxDepth = options._maxDepth; |
| | 0 | 145 | | _allowOutOfOrderMetadataProperties = options._allowOutOfOrderMetadataProperties; |
| | 0 | 146 | | _allowTrailingCommas = options._allowTrailingCommas; |
| | 0 | 147 | | _respectNullableAnnotations = options._respectNullableAnnotations; |
| | 0 | 148 | | _respectRequiredConstructorParameters = options._respectRequiredConstructorParameters; |
| | 0 | 149 | | _ignoreNullValues = options._ignoreNullValues; |
| | 0 | 150 | | _ignoreReadOnlyProperties = options._ignoreReadOnlyProperties; |
| | 0 | 151 | | _ignoreReadonlyFields = options._ignoreReadonlyFields; |
| | 0 | 152 | | _includeFields = options._includeFields; |
| | 0 | 153 | | _newLine = options._newLine; |
| | 0 | 154 | | _propertyNameCaseInsensitive = options._propertyNameCaseInsensitive; |
| | 0 | 155 | | _writeIndented = options._writeIndented; |
| | 0 | 156 | | _indentCharacter = options._indentCharacter; |
| | 0 | 157 | | _indentSize = options._indentSize; |
| | 0 | 158 | | _allowDuplicateProperties = options._allowDuplicateProperties; |
| | 0 | 159 | | _typeInfoResolver = options._typeInfoResolver; |
| | 0 | 160 | | EffectiveMaxDepth = options.EffectiveMaxDepth; |
| | 0 | 161 | | ReferenceHandlingStrategy = options.ReferenceHandlingStrategy; |
| | | 162 | | |
| | 0 | 163 | | TrackOptionsInstance(this); |
| | 0 | 164 | | } |
| | | 165 | | |
| | | 166 | | /// <summary> |
| | | 167 | | /// Constructs a new <see cref="JsonSerializerOptions"/> instance with a predefined set of options determined by |
| | | 168 | | /// </summary> |
| | | 169 | | /// <param name="defaults"> The <see cref="JsonSerializerDefaults"/> to reason about.</param> |
| | 0 | 170 | | public JsonSerializerOptions(JsonSerializerDefaults defaults) : this() |
| | 0 | 171 | | { |
| | | 172 | | // Should be kept in sync with equivalent overload in JsonSourceGenerationOptionsAttribute |
| | | 173 | | |
| | 0 | 174 | | if (defaults == JsonSerializerDefaults.Web) |
| | 0 | 175 | | { |
| | 0 | 176 | | _propertyNameCaseInsensitive = true; |
| | 0 | 177 | | _jsonPropertyNamingPolicy = JsonNamingPolicy.CamelCase; |
| | 0 | 178 | | _numberHandling = JsonNumberHandling.AllowReadingFromString; |
| | 0 | 179 | | } |
| | 0 | 180 | | else if (defaults == JsonSerializerDefaults.Strict) |
| | 0 | 181 | | { |
| | 0 | 182 | | _unmappedMemberHandling = JsonUnmappedMemberHandling.Disallow; |
| | 0 | 183 | | _allowDuplicateProperties = false; |
| | 0 | 184 | | _respectNullableAnnotations = true; |
| | 0 | 185 | | _respectRequiredConstructorParameters = true; |
| | 0 | 186 | | } |
| | 0 | 187 | | else if (defaults != JsonSerializerDefaults.General) |
| | 0 | 188 | | { |
| | 0 | 189 | | throw new ArgumentOutOfRangeException(nameof(defaults)); |
| | | 190 | | } |
| | 0 | 191 | | } |
| | | 192 | | |
| | | 193 | | /// <summary>Tracks the options instance to enable all instances to be enumerated.</summary> |
| | 682 | 194 | | private static void TrackOptionsInstance(JsonSerializerOptions options) => TrackedOptionsInstances.All.Add(optio |
| | | 195 | | |
| | | 196 | | internal static class TrackedOptionsInstances |
| | | 197 | | { |
| | | 198 | | /// <summary>Tracks all live JsonSerializerOptions instances.</summary> |
| | | 199 | | /// <remarks>Instances are added to the table in their constructor.</remarks> |
| | 682 | 200 | | public static ConditionalWeakTable<JsonSerializerOptions, object?> All { get; } = |
| | | 201 | | // TODO https://github.com/dotnet/runtime/issues/51159: |
| | | 202 | | // Look into linking this away / disabling it when hot reload isn't in use. |
| | 1 | 203 | | new ConditionalWeakTable<JsonSerializerOptions, object?>(); |
| | | 204 | | } |
| | | 205 | | |
| | | 206 | | /// <summary> |
| | | 207 | | /// Binds current <see cref="JsonSerializerOptions"/> instance with a new instance of the specified <see cref="S |
| | | 208 | | /// </summary> |
| | | 209 | | /// <typeparam name="TContext">The generic definition of the specified context type.</typeparam> |
| | | 210 | | /// <remarks> |
| | | 211 | | /// When serializing and deserializing types using the options |
| | | 212 | | /// instance, metadata for the types will be fetched from the context instance. |
| | | 213 | | /// </remarks> |
| | | 214 | | [Obsolete(Obsoletions.JsonSerializerOptionsAddContextMessage, DiagnosticId = Obsoletions.JsonSerializerOptionsAd |
| | | 215 | | [EditorBrowsable(EditorBrowsableState.Never)] |
| | | 216 | | public void AddContext<TContext>() where TContext : JsonSerializerContext, new() |
| | 0 | 217 | | { |
| | 0 | 218 | | VerifyMutable(); |
| | 0 | 219 | | TContext context = new(); |
| | 0 | 220 | | context.AssociateWithOptions(this); |
| | 0 | 221 | | } |
| | | 222 | | |
| | | 223 | | /// <summary> |
| | | 224 | | /// Gets or sets the <see cref="JsonTypeInfo"/> contract resolver used by this instance. |
| | | 225 | | /// </summary> |
| | | 226 | | /// <exception cref="InvalidOperationException"> |
| | | 227 | | /// Thrown if this property is set after serialization or deserialization has occurred. |
| | | 228 | | /// </exception> |
| | | 229 | | /// <remarks> |
| | | 230 | | /// A <see langword="null"/> setting is equivalent to using the reflection-based <see cref="DefaultJsonTypeInfoR |
| | | 231 | | /// The property will be populated automatically once used with one of the <see cref="JsonSerializer"/> methods. |
| | | 232 | | /// |
| | | 233 | | /// This property is kept in sync with the <see cref="TypeInfoResolverChain"/> property. |
| | | 234 | | /// Any change made to this property will be reflected by <see cref="TypeInfoResolverChain"/> and vice versa. |
| | | 235 | | /// </remarks> |
| | | 236 | | public IJsonTypeInfoResolver? TypeInfoResolver |
| | | 237 | | { |
| | | 238 | | get |
| | 12523 | 239 | | { |
| | 12523 | 240 | | return _typeInfoResolver; |
| | 12523 | 241 | | } |
| | | 242 | | set |
| | 0 | 243 | | { |
| | 0 | 244 | | VerifyMutable(); |
| | | 245 | | |
| | 0 | 246 | | if (_typeInfoResolverChain is { } resolverChain && !ReferenceEquals(resolverChain, value)) |
| | 0 | 247 | | { |
| | | 248 | | // User is setting a new resolver; detach the resolver chain if already created. |
| | 0 | 249 | | resolverChain.DetachFromOptions(); |
| | 0 | 250 | | _typeInfoResolverChain = null; |
| | 0 | 251 | | } |
| | | 252 | | |
| | 0 | 253 | | _typeInfoResolver = value; |
| | 0 | 254 | | } |
| | | 255 | | } |
| | | 256 | | |
| | | 257 | | /// <summary> |
| | | 258 | | /// Gets the list of chained <see cref="JsonTypeInfo"/> contract resolvers used by this instance. |
| | | 259 | | /// </summary> |
| | | 260 | | /// <remarks> |
| | | 261 | | /// The ordering of the chain is significant: <see cref="JsonSerializerOptions "/> will query each |
| | | 262 | | /// of the resolvers in their specified order, returning the first result that is non-null. |
| | | 263 | | /// If all resolvers in the chain return null, then <see cref="JsonSerializerOptions"/> will also return null. |
| | | 264 | | /// |
| | | 265 | | /// This property is auxiliary to and is kept in sync with the <see cref="TypeInfoResolver"/> property. |
| | | 266 | | /// Any change made to this property will be reflected by <see cref="TypeInfoResolver"/> and vice versa. |
| | | 267 | | /// </remarks> |
| | 0 | 268 | | public IList<IJsonTypeInfoResolver> TypeInfoResolverChain => _typeInfoResolverChain ??= new(this); |
| | | 269 | | private OptionsBoundJsonTypeInfoResolverChain? _typeInfoResolverChain; |
| | | 270 | | |
| | | 271 | | /// <summary> |
| | | 272 | | /// Allows JSON metadata properties to be specified after regular properties in a deserialized JSON object. |
| | | 273 | | /// </summary> |
| | | 274 | | /// <exception cref="InvalidOperationException"> |
| | | 275 | | /// Thrown if this property is set after serialization or deserialization has occurred. |
| | | 276 | | /// </exception> |
| | | 277 | | /// <remarks> |
| | | 278 | | /// When set to <see langword="true" />, removes the requirement that JSON metadata properties |
| | | 279 | | /// such as $id and $type should be specified at the very start of the deserialized JSON object. |
| | | 280 | | /// |
| | | 281 | | /// It should be noted that enabling this setting can result in over-buffering |
| | | 282 | | /// when deserializing large JSON payloads in the context of streaming deserialization. |
| | | 283 | | /// </remarks> |
| | | 284 | | public bool AllowOutOfOrderMetadataProperties |
| | | 285 | | { |
| | | 286 | | get |
| | 0 | 287 | | { |
| | 0 | 288 | | return _allowOutOfOrderMetadataProperties; |
| | 0 | 289 | | } |
| | | 290 | | set |
| | 0 | 291 | | { |
| | 0 | 292 | | VerifyMutable(); |
| | 0 | 293 | | _allowOutOfOrderMetadataProperties = value; |
| | 0 | 294 | | } |
| | | 295 | | } |
| | | 296 | | |
| | | 297 | | /// <summary> |
| | | 298 | | /// Defines whether an extra comma at the end of a list of JSON values in an object or array |
| | | 299 | | /// is allowed (and ignored) within the JSON payload being deserialized. |
| | | 300 | | /// </summary> |
| | | 301 | | /// <exception cref="InvalidOperationException"> |
| | | 302 | | /// Thrown if this property is set after serialization or deserialization has occurred. |
| | | 303 | | /// </exception> |
| | | 304 | | /// <remarks> |
| | | 305 | | /// By default, it's set to false, and <exception cref="JsonException"/> is thrown if a trailing comma is encoun |
| | | 306 | | /// </remarks> |
| | | 307 | | public bool AllowTrailingCommas |
| | | 308 | | { |
| | | 309 | | get |
| | 55924 | 310 | | { |
| | 55924 | 311 | | return _allowTrailingCommas; |
| | 55924 | 312 | | } |
| | | 313 | | set |
| | 682 | 314 | | { |
| | 682 | 315 | | VerifyMutable(); |
| | 682 | 316 | | _allowTrailingCommas = value; |
| | 682 | 317 | | } |
| | | 318 | | } |
| | | 319 | | |
| | | 320 | | /// <summary> |
| | | 321 | | /// The default buffer size in bytes used when creating temporary buffers. |
| | | 322 | | /// </summary> |
| | | 323 | | /// <remarks>The default size is 16K.</remarks> |
| | | 324 | | /// <exception cref="System.ArgumentException">Thrown when the buffer size is less than 1.</exception> |
| | | 325 | | /// <exception cref="InvalidOperationException"> |
| | | 326 | | /// Thrown if this property is set after serialization or deserialization has occurred. |
| | | 327 | | /// </exception> |
| | | 328 | | public int DefaultBufferSize |
| | | 329 | | { |
| | | 330 | | get |
| | 27962 | 331 | | { |
| | 27962 | 332 | | return _defaultBufferSize; |
| | 27962 | 333 | | } |
| | | 334 | | set |
| | 0 | 335 | | { |
| | 0 | 336 | | VerifyMutable(); |
| | | 337 | | |
| | 0 | 338 | | if (value < 1) |
| | 0 | 339 | | { |
| | 0 | 340 | | throw new ArgumentException(SR.SerializationInvalidBufferSize); |
| | | 341 | | } |
| | | 342 | | |
| | 0 | 343 | | _defaultBufferSize = value; |
| | 0 | 344 | | } |
| | | 345 | | } |
| | | 346 | | |
| | | 347 | | /// <summary> |
| | | 348 | | /// The encoder to use when escaping strings, or <see langword="null" /> to use the default encoder. |
| | | 349 | | /// </summary> |
| | | 350 | | public JavaScriptEncoder? Encoder |
| | | 351 | | { |
| | | 352 | | get |
| | 8332 | 353 | | { |
| | 8332 | 354 | | return _encoder; |
| | 8332 | 355 | | } |
| | | 356 | | set |
| | 682 | 357 | | { |
| | 682 | 358 | | VerifyMutable(); |
| | | 359 | | |
| | 682 | 360 | | _encoder = value; |
| | 682 | 361 | | } |
| | | 362 | | } |
| | | 363 | | |
| | | 364 | | /// <summary> |
| | | 365 | | /// Specifies the policy used to convert a <see cref="System.Collections.IDictionary"/> key's name to another fo |
| | | 366 | | /// </summary> |
| | | 367 | | /// <remarks> |
| | | 368 | | /// This property can be set to <see cref="JsonNamingPolicy.CamelCase"/> to specify a camel-casing policy. |
| | | 369 | | /// It is not used when deserializing. |
| | | 370 | | /// </remarks> |
| | | 371 | | public JsonNamingPolicy? DictionaryKeyPolicy |
| | | 372 | | { |
| | | 373 | | get |
| | 0 | 374 | | { |
| | 0 | 375 | | return _dictionaryKeyPolicy; |
| | 0 | 376 | | } |
| | | 377 | | set |
| | 0 | 378 | | { |
| | 0 | 379 | | VerifyMutable(); |
| | 0 | 380 | | _dictionaryKeyPolicy = value; |
| | 0 | 381 | | } |
| | | 382 | | } |
| | | 383 | | |
| | | 384 | | /// <summary> |
| | | 385 | | /// Determines whether null values are ignored during serialization and deserialization. |
| | | 386 | | /// The default value is false. |
| | | 387 | | /// </summary> |
| | | 388 | | /// <exception cref="InvalidOperationException"> |
| | | 389 | | /// Thrown if this property is set after serialization or deserialization has occurred. |
| | | 390 | | /// or <see cref="DefaultIgnoreCondition"/> has been set to a non-default value. These properties cannot be used |
| | | 391 | | /// </exception> |
| | | 392 | | [Obsolete(Obsoletions.JsonSerializerOptionsIgnoreNullValuesMessage, DiagnosticId = Obsoletions.JsonSerializerOpt |
| | | 393 | | [EditorBrowsable(EditorBrowsableState.Never)] |
| | | 394 | | public bool IgnoreNullValues |
| | | 395 | | { |
| | | 396 | | get |
| | 20048 | 397 | | { |
| | 20048 | 398 | | return _ignoreNullValues; |
| | 20048 | 399 | | } |
| | | 400 | | set |
| | 0 | 401 | | { |
| | 0 | 402 | | VerifyMutable(); |
| | | 403 | | |
| | 0 | 404 | | if (value && _defaultIgnoreCondition != JsonIgnoreCondition.Never) |
| | 0 | 405 | | { |
| | 0 | 406 | | throw new InvalidOperationException(SR.DefaultIgnoreConditionAlreadySpecified); |
| | | 407 | | } |
| | | 408 | | |
| | 0 | 409 | | _ignoreNullValues = value; |
| | 0 | 410 | | } |
| | | 411 | | } |
| | | 412 | | |
| | | 413 | | /// <summary> |
| | | 414 | | /// Specifies a condition to determine when properties with default values are ignored during serialization or d |
| | | 415 | | /// The default value is <see cref="JsonIgnoreCondition.Never" />. |
| | | 416 | | /// </summary> |
| | | 417 | | /// <exception cref="ArgumentException"> |
| | | 418 | | /// Thrown if this property is set to <see cref="JsonIgnoreCondition.Always"/>. |
| | | 419 | | /// </exception> |
| | | 420 | | /// <exception cref="InvalidOperationException"> |
| | | 421 | | /// Thrown if this property is set after serialization or deserialization has occurred, |
| | | 422 | | /// or <see cref="IgnoreNullValues"/> has been set to <see langword="true"/>. These properties cannot be used to |
| | | 423 | | /// </exception> |
| | | 424 | | public JsonIgnoreCondition DefaultIgnoreCondition |
| | | 425 | | { |
| | | 426 | | get |
| | 40096 | 427 | | { |
| | 40096 | 428 | | return _defaultIgnoreCondition; |
| | 40096 | 429 | | } |
| | | 430 | | set |
| | 0 | 431 | | { |
| | 0 | 432 | | VerifyMutable(); |
| | | 433 | | |
| | 0 | 434 | | if (value == JsonIgnoreCondition.Always) |
| | 0 | 435 | | { |
| | 0 | 436 | | throw new ArgumentException(SR.DefaultIgnoreConditionInvalid); |
| | | 437 | | } |
| | | 438 | | |
| | 0 | 439 | | if (value != JsonIgnoreCondition.Never && _ignoreNullValues) |
| | 0 | 440 | | { |
| | 0 | 441 | | throw new InvalidOperationException(SR.DefaultIgnoreConditionAlreadySpecified); |
| | | 442 | | } |
| | | 443 | | |
| | 0 | 444 | | _defaultIgnoreCondition = value; |
| | 0 | 445 | | } |
| | | 446 | | } |
| | | 447 | | |
| | | 448 | | /// <summary> |
| | | 449 | | /// Specifies how number types should be handled when serializing or deserializing. |
| | | 450 | | /// </summary> |
| | | 451 | | /// <exception cref="InvalidOperationException"> |
| | | 452 | | /// Thrown if this property is set after serialization or deserialization has occurred. |
| | | 453 | | /// </exception> |
| | | 454 | | public JsonNumberHandling NumberHandling |
| | | 455 | | { |
| | 21771 | 456 | | get => _numberHandling; |
| | | 457 | | set |
| | 682 | 458 | | { |
| | 682 | 459 | | VerifyMutable(); |
| | | 460 | | |
| | 682 | 461 | | if (!JsonSerializer.IsValidNumberHandlingValue(value)) |
| | 0 | 462 | | { |
| | 0 | 463 | | throw new ArgumentOutOfRangeException(nameof(value)); |
| | | 464 | | } |
| | 682 | 465 | | _numberHandling = value; |
| | 682 | 466 | | } |
| | | 467 | | } |
| | | 468 | | |
| | | 469 | | /// <summary> |
| | | 470 | | /// Specifies preferred object creation handling for properties when deserializing JSON. |
| | | 471 | | /// When set to <see cref="JsonObjectCreationHandling.Populate"/> all properties which |
| | | 472 | | /// are capable of reusing the existing instance will be populated. |
| | | 473 | | /// </summary> |
| | | 474 | | /// <remarks> |
| | | 475 | | /// Only property type is taken into consideration. For example if property is of type |
| | | 476 | | /// <see cref="IEnumerable{T}"/> but it is assigned <see cref="List{T}"/> it will not be populated |
| | | 477 | | /// because <see cref="IEnumerable{T}"/> is not capable of populating. |
| | | 478 | | /// Additionally value types require a setter to be populated. |
| | | 479 | | /// </remarks> |
| | | 480 | | public JsonObjectCreationHandling PreferredObjectCreationHandling |
| | | 481 | | { |
| | 14805 | 482 | | get => _preferredObjectCreationHandling; |
| | | 483 | | set |
| | 0 | 484 | | { |
| | 0 | 485 | | VerifyMutable(); |
| | | 486 | | |
| | 0 | 487 | | if (!JsonSerializer.IsValidCreationHandlingValue(value)) |
| | 0 | 488 | | { |
| | 0 | 489 | | throw new ArgumentOutOfRangeException(nameof(value)); |
| | | 490 | | } |
| | | 491 | | |
| | 0 | 492 | | _preferredObjectCreationHandling = value; |
| | 0 | 493 | | } |
| | | 494 | | } |
| | | 495 | | |
| | | 496 | | /// <summary> |
| | | 497 | | /// Determines whether read-only properties are ignored during serialization. |
| | | 498 | | /// A property is read-only if it contains a public getter but not a public setter. |
| | | 499 | | /// The default value is false. |
| | | 500 | | /// </summary> |
| | | 501 | | /// <remarks> |
| | | 502 | | /// Read-only properties are not deserialized regardless of this setting. |
| | | 503 | | /// </remarks> |
| | | 504 | | /// <exception cref="InvalidOperationException"> |
| | | 505 | | /// Thrown if this property is set after serialization or deserialization has occurred. |
| | | 506 | | /// </exception> |
| | | 507 | | public bool IgnoreReadOnlyProperties |
| | | 508 | | { |
| | | 509 | | get |
| | 0 | 510 | | { |
| | 0 | 511 | | return _ignoreReadOnlyProperties; |
| | 0 | 512 | | } |
| | | 513 | | set |
| | 0 | 514 | | { |
| | 0 | 515 | | VerifyMutable(); |
| | 0 | 516 | | _ignoreReadOnlyProperties = value; |
| | 0 | 517 | | } |
| | | 518 | | } |
| | | 519 | | |
| | | 520 | | /// <summary> |
| | | 521 | | /// Determines whether read-only fields are ignored during serialization. |
| | | 522 | | /// A field is read-only if it is marked with the <c>readonly</c> keyword. |
| | | 523 | | /// The default value is false. |
| | | 524 | | /// </summary> |
| | | 525 | | /// <remarks> |
| | | 526 | | /// Read-only fields are not deserialized regardless of this setting. |
| | | 527 | | /// </remarks> |
| | | 528 | | /// <exception cref="InvalidOperationException"> |
| | | 529 | | /// Thrown if this property is set after serialization or deserialization has occurred. |
| | | 530 | | /// </exception> |
| | | 531 | | public bool IgnoreReadOnlyFields |
| | | 532 | | { |
| | | 533 | | get |
| | 0 | 534 | | { |
| | 0 | 535 | | return _ignoreReadonlyFields; |
| | 0 | 536 | | } |
| | | 537 | | set |
| | 0 | 538 | | { |
| | 0 | 539 | | VerifyMutable(); |
| | 0 | 540 | | _ignoreReadonlyFields = value; |
| | 0 | 541 | | } |
| | | 542 | | } |
| | | 543 | | |
| | | 544 | | /// <summary> |
| | | 545 | | /// Determines whether fields are handled on serialization and deserialization. |
| | | 546 | | /// The default value is false. |
| | | 547 | | /// </summary> |
| | | 548 | | /// <exception cref="InvalidOperationException"> |
| | | 549 | | /// Thrown if this property is set after serialization or deserialization has occurred. |
| | | 550 | | /// </exception> |
| | | 551 | | public bool IncludeFields |
| | | 552 | | { |
| | | 553 | | get |
| | 0 | 554 | | { |
| | 0 | 555 | | return _includeFields; |
| | 0 | 556 | | } |
| | | 557 | | set |
| | 0 | 558 | | { |
| | 0 | 559 | | VerifyMutable(); |
| | 0 | 560 | | _includeFields = value; |
| | 0 | 561 | | } |
| | | 562 | | } |
| | | 563 | | |
| | | 564 | | /// <summary> |
| | | 565 | | /// Gets or sets the maximum depth allowed when serializing or deserializing JSON, with the default (i.e. 0) ind |
| | | 566 | | /// </summary> |
| | | 567 | | /// <exception cref="InvalidOperationException"> |
| | | 568 | | /// Thrown if this property is set after serialization or deserialization has occurred. |
| | | 569 | | /// </exception> |
| | | 570 | | /// <exception cref="ArgumentOutOfRangeException"> |
| | | 571 | | /// Thrown when the max depth is set to a negative value. |
| | | 572 | | /// </exception> |
| | | 573 | | /// <remarks> |
| | | 574 | | /// Going past this depth will throw a <exception cref="JsonException"/>. |
| | | 575 | | /// </remarks> |
| | | 576 | | public int MaxDepth |
| | | 577 | | { |
| | 0 | 578 | | get => _maxDepth; |
| | | 579 | | set |
| | 0 | 580 | | { |
| | 0 | 581 | | VerifyMutable(); |
| | | 582 | | |
| | 0 | 583 | | if (value < 0) |
| | 0 | 584 | | { |
| | 0 | 585 | | ThrowHelper.ThrowArgumentOutOfRangeException_MaxDepthMustBePositive(nameof(value)); |
| | | 586 | | } |
| | | 587 | | |
| | 0 | 588 | | _maxDepth = value; |
| | 0 | 589 | | EffectiveMaxDepth = (value == 0 ? DefaultMaxDepth : value); |
| | 0 | 590 | | } |
| | | 591 | | } |
| | | 592 | | |
| | 56606 | 593 | | internal int EffectiveMaxDepth { get; private set; } = DefaultMaxDepth; |
| | | 594 | | |
| | | 595 | | /// <summary> |
| | | 596 | | /// Specifies the policy used to convert a property's name on an object to another format, such as camel-casing. |
| | | 597 | | /// The resulting property name is expected to match the JSON payload during deserialization, and |
| | | 598 | | /// will be used when writing the property name during serialization. |
| | | 599 | | /// </summary> |
| | | 600 | | /// <remarks> |
| | | 601 | | /// The policy is not used for properties that have a <see cref="JsonPropertyNameAttribute"/> applied. |
| | | 602 | | /// This property can be set to <see cref="JsonNamingPolicy.CamelCase"/> to specify a camel-casing policy. |
| | | 603 | | /// </remarks> |
| | | 604 | | public JsonNamingPolicy? PropertyNamingPolicy |
| | | 605 | | { |
| | | 606 | | get |
| | 7794 | 607 | | { |
| | 7794 | 608 | | return _jsonPropertyNamingPolicy; |
| | 7794 | 609 | | } |
| | | 610 | | set |
| | 0 | 611 | | { |
| | 0 | 612 | | VerifyMutable(); |
| | 0 | 613 | | _jsonPropertyNamingPolicy = value; |
| | 0 | 614 | | } |
| | | 615 | | } |
| | | 616 | | |
| | | 617 | | /// <summary> |
| | | 618 | | /// Determines whether a property's name uses a case-insensitive comparison during deserialization. |
| | | 619 | | /// The default value is false. |
| | | 620 | | /// </summary> |
| | | 621 | | /// <remarks>There is a performance cost associated when the value is true.</remarks> |
| | | 622 | | public bool PropertyNameCaseInsensitive |
| | | 623 | | { |
| | | 624 | | get |
| | 3888 | 625 | | { |
| | 3888 | 626 | | return _propertyNameCaseInsensitive; |
| | 3888 | 627 | | } |
| | | 628 | | set |
| | 0 | 629 | | { |
| | 0 | 630 | | VerifyMutable(); |
| | 0 | 631 | | _propertyNameCaseInsensitive = value; |
| | 0 | 632 | | } |
| | | 633 | | } |
| | | 634 | | |
| | | 635 | | /// <summary> |
| | | 636 | | /// Defines how the comments are handled during deserialization. |
| | | 637 | | /// </summary> |
| | | 638 | | /// <exception cref="InvalidOperationException"> |
| | | 639 | | /// Thrown if this property is set after serialization or deserialization has occurred. |
| | | 640 | | /// </exception> |
| | | 641 | | /// <exception cref="ArgumentOutOfRangeException"> |
| | | 642 | | /// Thrown when the comment handling enum is set to a value that is not supported (or not within the <see cref=" |
| | | 643 | | /// </exception> |
| | | 644 | | /// <remarks> |
| | | 645 | | /// By default <exception cref="JsonException"/> is thrown if a comment is encountered. |
| | | 646 | | /// </remarks> |
| | | 647 | | public JsonCommentHandling ReadCommentHandling |
| | | 648 | | { |
| | | 649 | | get |
| | 55924 | 650 | | { |
| | 55924 | 651 | | return _readCommentHandling; |
| | 55924 | 652 | | } |
| | | 653 | | set |
| | 682 | 654 | | { |
| | 682 | 655 | | VerifyMutable(); |
| | | 656 | | |
| | 682 | 657 | | Debug.Assert(value >= 0); |
| | 682 | 658 | | if (value > JsonCommentHandling.Skip) |
| | 0 | 659 | | throw new ArgumentOutOfRangeException(nameof(value), SR.JsonSerializerDoesNotSupportComments); |
| | | 660 | | |
| | 682 | 661 | | _readCommentHandling = value; |
| | 682 | 662 | | } |
| | | 663 | | } |
| | | 664 | | |
| | | 665 | | /// <summary> |
| | | 666 | | /// Defines how deserializing a type declared as an <see cref="object"/> is handled during deserialization. |
| | | 667 | | /// </summary> |
| | | 668 | | public JsonUnknownTypeHandling UnknownTypeHandling |
| | | 669 | | { |
| | 634 | 670 | | get => _unknownTypeHandling; |
| | | 671 | | set |
| | 0 | 672 | | { |
| | 0 | 673 | | VerifyMutable(); |
| | 0 | 674 | | _unknownTypeHandling = value; |
| | 0 | 675 | | } |
| | | 676 | | } |
| | | 677 | | |
| | | 678 | | /// <summary> |
| | | 679 | | /// Determines how <see cref="JsonSerializer"/> handles JSON properties that |
| | | 680 | | /// cannot be mapped to a specific .NET member when deserializing object types. |
| | | 681 | | /// </summary> |
| | | 682 | | public JsonUnmappedMemberHandling UnmappedMemberHandling |
| | | 683 | | { |
| | 1299 | 684 | | get => _unmappedMemberHandling; |
| | | 685 | | set |
| | 0 | 686 | | { |
| | 0 | 687 | | VerifyMutable(); |
| | 0 | 688 | | _unmappedMemberHandling = value; |
| | 0 | 689 | | } |
| | | 690 | | } |
| | | 691 | | |
| | | 692 | | /// <summary> |
| | | 693 | | /// Defines whether JSON should pretty print which includes: |
| | | 694 | | /// indenting nested JSON tokens, adding new lines, and adding white space between property names and values. |
| | | 695 | | /// By default, the JSON is serialized without any extra white space. |
| | | 696 | | /// </summary> |
| | | 697 | | /// <exception cref="InvalidOperationException"> |
| | | 698 | | /// Thrown if this property is set after serialization or deserialization has occurred. |
| | | 699 | | /// </exception> |
| | | 700 | | public bool WriteIndented |
| | | 701 | | { |
| | | 702 | | get |
| | 0 | 703 | | { |
| | 0 | 704 | | return _writeIndented; |
| | 0 | 705 | | } |
| | | 706 | | set |
| | 0 | 707 | | { |
| | 0 | 708 | | VerifyMutable(); |
| | 0 | 709 | | _writeIndented = value; |
| | 0 | 710 | | } |
| | | 711 | | } |
| | | 712 | | |
| | | 713 | | /// <summary> |
| | | 714 | | /// Defines the indentation character being used when <see cref="WriteIndented" /> is enabled. Defaults to the s |
| | | 715 | | /// </summary> |
| | | 716 | | /// <remarks>Allowed characters are space and horizontal tab.</remarks> |
| | | 717 | | /// <exception cref="ArgumentOutOfRangeException"><paramref name="value"/> contains an invalid character.</excep |
| | | 718 | | /// <exception cref="InvalidOperationException"> |
| | | 719 | | /// Thrown if this property is set after serialization or deserialization has occurred. |
| | | 720 | | /// </exception> |
| | | 721 | | public char IndentCharacter |
| | | 722 | | { |
| | | 723 | | get |
| | 0 | 724 | | { |
| | 0 | 725 | | return _indentCharacter; |
| | 0 | 726 | | } |
| | | 727 | | set |
| | 0 | 728 | | { |
| | 0 | 729 | | JsonWriterHelper.ValidateIndentCharacter(value); |
| | 0 | 730 | | VerifyMutable(); |
| | 0 | 731 | | _indentCharacter = value; |
| | 0 | 732 | | } |
| | | 733 | | } |
| | | 734 | | |
| | | 735 | | /// <summary> |
| | | 736 | | /// Defines the indentation size being used when <see cref="WriteIndented" /> is enabled. Defaults to two. |
| | | 737 | | /// </summary> |
| | | 738 | | /// <remarks>Allowed values are all integers between 0 and 127, included.</remarks> |
| | | 739 | | /// <exception cref="ArgumentOutOfRangeException"><paramref name="value"/> is out of the allowed range.</excepti |
| | | 740 | | /// <exception cref="InvalidOperationException"> |
| | | 741 | | /// Thrown if this property is set after serialization or deserialization has occurred. |
| | | 742 | | /// </exception> |
| | | 743 | | public int IndentSize |
| | | 744 | | { |
| | | 745 | | get |
| | 0 | 746 | | { |
| | 0 | 747 | | return _indentSize; |
| | 0 | 748 | | } |
| | | 749 | | set |
| | 0 | 750 | | { |
| | 0 | 751 | | JsonWriterHelper.ValidateIndentSize(value); |
| | 0 | 752 | | VerifyMutable(); |
| | 0 | 753 | | _indentSize = value; |
| | 0 | 754 | | } |
| | | 755 | | } |
| | | 756 | | |
| | | 757 | | /// <summary> |
| | | 758 | | /// Configures how object references are handled when reading and writing JSON. |
| | | 759 | | /// </summary> |
| | | 760 | | public ReferenceHandler? ReferenceHandler |
| | | 761 | | { |
| | 0 | 762 | | get => _referenceHandler; |
| | | 763 | | set |
| | 0 | 764 | | { |
| | 0 | 765 | | VerifyMutable(); |
| | 0 | 766 | | _referenceHandler = value; |
| | 0 | 767 | | ReferenceHandlingStrategy = value?.HandlingStrategy ?? JsonKnownReferenceHandler.Unspecified; |
| | 0 | 768 | | } |
| | | 769 | | } |
| | | 770 | | |
| | | 771 | | /// <summary> |
| | | 772 | | /// Gets or sets the new line string to use when <see cref="WriteIndented"/> is <see langword="true"/>. |
| | | 773 | | /// The default is the value of <see cref="Environment.NewLine"/>. |
| | | 774 | | /// </summary> |
| | | 775 | | /// <exception cref="ArgumentNullException"> |
| | | 776 | | /// Thrown when the new line string is <see langword="null"/>. |
| | | 777 | | /// </exception> |
| | | 778 | | /// <exception cref="ArgumentOutOfRangeException"> |
| | | 779 | | /// Thrown when the new line string is not <c>\n</c> or <c>\r\n</c>. |
| | | 780 | | /// </exception> |
| | | 781 | | /// <exception cref="InvalidOperationException"> |
| | | 782 | | /// Thrown if this property is set after serialization or deserialization has occurred. |
| | | 783 | | /// </exception> |
| | | 784 | | public string NewLine |
| | | 785 | | { |
| | | 786 | | get |
| | 1508 | 787 | | { |
| | 1508 | 788 | | return _newLine ??= Environment.NewLine; |
| | 1508 | 789 | | } |
| | | 790 | | set |
| | 0 | 791 | | { |
| | 0 | 792 | | JsonWriterHelper.ValidateNewLine(value); |
| | 0 | 793 | | VerifyMutable(); |
| | 0 | 794 | | _newLine = value; |
| | 0 | 795 | | } |
| | | 796 | | } |
| | | 797 | | |
| | | 798 | | /// <summary> |
| | | 799 | | /// Gets or sets a value that indicates whether nullability annotations should be respected during serialization |
| | | 800 | | /// </summary> |
| | | 801 | | /// <exception cref="InvalidOperationException"> |
| | | 802 | | /// Thrown if this property is set after serialization or deserialization has occurred. |
| | | 803 | | /// </exception> |
| | | 804 | | /// <remarks> |
| | | 805 | | /// Nullability annotations are resolved from the properties, fields and constructor parameters |
| | | 806 | | /// that are used by the serializer. This includes annotations stemming from attributes such as |
| | | 807 | | /// <see cref="NotNullAttribute"/>, <see cref="MaybeNullAttribute"/>, |
| | | 808 | | /// <see cref="AllowNullAttribute"/> and <see cref="DisallowNullAttribute"/>. |
| | | 809 | | /// |
| | | 810 | | /// Due to restrictions in how nullable reference types are represented at run time, |
| | | 811 | | /// this setting only governs nullability annotations of non-generic properties and fields. |
| | | 812 | | /// It cannot be used to enforce nullability annotations of root-level types or generic parameters. |
| | | 813 | | /// |
| | | 814 | | /// The default setting for this property can be toggled application-wide using the |
| | | 815 | | /// "System.Text.Json.Serialization.RespectNullableAnnotationsDefault" feature switch. |
| | | 816 | | /// </remarks> |
| | | 817 | | public bool RespectNullableAnnotations |
| | | 818 | | { |
| | 0 | 819 | | get => _respectNullableAnnotations; |
| | | 820 | | set |
| | 0 | 821 | | { |
| | 0 | 822 | | VerifyMutable(); |
| | 0 | 823 | | _respectNullableAnnotations = value; |
| | 0 | 824 | | } |
| | | 825 | | } |
| | | 826 | | |
| | | 827 | | /// <summary> |
| | | 828 | | /// Gets or sets a value that indicates whether non-optional constructor parameters should be specified during d |
| | | 829 | | /// </summary> |
| | | 830 | | /// <exception cref="InvalidOperationException"> |
| | | 831 | | /// Thrown if this property is set after serialization or deserialization has occurred. |
| | | 832 | | /// </exception> |
| | | 833 | | /// <remarks> |
| | | 834 | | /// For historical reasons constructor-based deserialization treats all constructor parameters as optional by de |
| | | 835 | | /// This flag allows users to toggle that behavior as necessary for each <see cref="JsonSerializerOptions"/> ins |
| | | 836 | | /// |
| | | 837 | | /// The default setting for this property can be toggled application-wide using the |
| | | 838 | | /// "System.Text.Json.Serialization.RespectRequiredConstructorParametersDefault" feature switch. |
| | | 839 | | /// </remarks> |
| | | 840 | | public bool RespectRequiredConstructorParameters |
| | | 841 | | { |
| | 4494 | 842 | | get => _respectRequiredConstructorParameters; |
| | | 843 | | set |
| | 0 | 844 | | { |
| | 0 | 845 | | VerifyMutable(); |
| | 0 | 846 | | _respectRequiredConstructorParameters = value; |
| | 0 | 847 | | } |
| | | 848 | | } |
| | | 849 | | |
| | | 850 | | /// <summary> |
| | | 851 | | /// Defines whether duplicate property names are allowed when deserializing JSON objects. |
| | | 852 | | /// </summary> |
| | | 853 | | /// <exception cref="InvalidOperationException"> |
| | | 854 | | /// Thrown if this property is set after serialization or deserialization has occurred. |
| | | 855 | | /// </exception> |
| | | 856 | | /// <remarks> |
| | | 857 | | /// <para> |
| | | 858 | | /// By default, it's set to true. If set to false, <see cref="JsonException"/> is thrown |
| | | 859 | | /// when a duplicate property name is encountered during deserialization. |
| | | 860 | | /// </para> |
| | | 861 | | /// <para> |
| | | 862 | | /// Duplicate property names are not allowed in serialization. |
| | | 863 | | /// </para> |
| | | 864 | | /// </remarks> |
| | | 865 | | public bool AllowDuplicateProperties |
| | | 866 | | { |
| | 3050 | 867 | | get => _allowDuplicateProperties; |
| | | 868 | | set |
| | 0 | 869 | | { |
| | 0 | 870 | | VerifyMutable(); |
| | 0 | 871 | | _allowDuplicateProperties = value; |
| | 0 | 872 | | } |
| | | 873 | | } |
| | | 874 | | |
| | | 875 | | /// <summary> |
| | | 876 | | /// Returns true if options uses compatible built-in resolvers or a combination of compatible built-in resolvers |
| | | 877 | | /// </summary> |
| | | 878 | | [DebuggerBrowsable(DebuggerBrowsableState.Never)] |
| | | 879 | | internal bool CanUseFastPathSerializationLogic |
| | | 880 | | { |
| | | 881 | | get |
| | 12254 | 882 | | { |
| | 12254 | 883 | | Debug.Assert(IsReadOnly); |
| | 12254 | 884 | | Debug.Assert(TypeInfoResolver != null); |
| | 12254 | 885 | | return _canUseFastPathSerializationLogic ??= TypeInfoResolver.IsCompatibleWithOptions(this); |
| | 12254 | 886 | | } |
| | | 887 | | } |
| | | 888 | | |
| | | 889 | | private bool? _canUseFastPathSerializationLogic; |
| | | 890 | | |
| | | 891 | | // The cached value used to determine if ReferenceHandler should use Preserve or IgnoreCycles semantics or None |
| | 682 | 892 | | internal JsonKnownReferenceHandler ReferenceHandlingStrategy = JsonKnownReferenceHandler.Unspecified; |
| | | 893 | | |
| | | 894 | | /// <summary> |
| | | 895 | | /// Specifies whether the current instance has been locked for user modification. |
| | | 896 | | /// </summary> |
| | | 897 | | /// <remarks> |
| | | 898 | | /// A <see cref="JsonSerializerOptions"/> instance can be locked either if |
| | | 899 | | /// it has been passed to one of the <see cref="JsonSerializer"/> methods, |
| | | 900 | | /// has been associated with a <see cref="JsonSerializerContext"/> instance, |
| | | 901 | | /// or a user explicitly called the <see cref="MakeReadOnly()"/> methods on the instance. |
| | | 902 | | /// |
| | | 903 | | /// Read-only instances use caching when querying <see cref="JsonConverter"/> and <see cref="JsonTypeInfo"/> met |
| | | 904 | | /// </remarks> |
| | 180374 | 905 | | public bool IsReadOnly => _isReadOnly; |
| | | 906 | | private volatile bool _isReadOnly; |
| | | 907 | | |
| | | 908 | | /// <summary> |
| | | 909 | | /// Marks the current instance as read-only preventing any further user modification. |
| | | 910 | | /// </summary> |
| | | 911 | | /// <exception cref="InvalidOperationException">The instance does not specify a <see cref="TypeInfoResolver"/> s |
| | | 912 | | /// <remarks>This method is idempotent.</remarks> |
| | | 913 | | public void MakeReadOnly() |
| | 24508 | 914 | | { |
| | 24508 | 915 | | if (_typeInfoResolver is null) |
| | 0 | 916 | | { |
| | 0 | 917 | | ThrowHelper.ThrowInvalidOperationException_JsonSerializerOptionsNoTypeInfoResolverSpecified(); |
| | | 918 | | } |
| | | 919 | | |
| | 24508 | 920 | | _isReadOnly = true; |
| | 24508 | 921 | | } |
| | | 922 | | |
| | | 923 | | /// <summary> |
| | | 924 | | /// Marks the current instance as read-only preventing any further user modification. |
| | | 925 | | /// </summary> |
| | | 926 | | /// <param name="populateMissingResolver">Populates unconfigured <see cref="TypeInfoResolver"/> properties with |
| | | 927 | | /// <exception cref="InvalidOperationException"> |
| | | 928 | | /// The instance does not specify a <see cref="TypeInfoResolver"/> setting. Thrown if <paramref name="populateMi |
| | | 929 | | /// -OR- |
| | | 930 | | /// The <see cref="JsonSerializer.IsReflectionEnabledByDefault"/> feature switch has been turned off. |
| | | 931 | | /// </exception> |
| | | 932 | | /// <remarks> |
| | | 933 | | /// When <paramref name="populateMissingResolver"/> is set to <see langword="true" />, configures the instance f |
| | | 934 | | /// the semantics of the <see cref="JsonSerializer"/> methods accepting <see cref="JsonSerializerOptions"/> para |
| | | 935 | | /// |
| | | 936 | | /// This method is idempotent. |
| | | 937 | | /// </remarks> |
| | | 938 | | [RequiresUnreferencedCode("Populating unconfigured TypeInfoResolver properties with the reflection resolver requ |
| | | 939 | | [RequiresDynamicCode("Populating unconfigured TypeInfoResolver properties with the reflection resolver requires |
| | | 940 | | public void MakeReadOnly(bool populateMissingResolver) |
| | 55924 | 941 | | { |
| | 55924 | 942 | | if (populateMissingResolver) |
| | 55924 | 943 | | { |
| | 55924 | 944 | | if (!_isConfiguredForJsonSerializer) |
| | 682 | 945 | | { |
| | 682 | 946 | | ConfigureForJsonSerializer(); |
| | 682 | 947 | | } |
| | 55924 | 948 | | } |
| | | 949 | | else |
| | 0 | 950 | | { |
| | 0 | 951 | | MakeReadOnly(); |
| | 0 | 952 | | } |
| | | 953 | | |
| | 55924 | 954 | | Debug.Assert(IsReadOnly); |
| | 55924 | 955 | | } |
| | | 956 | | |
| | | 957 | | /// <summary> |
| | | 958 | | /// Configures the instance for use by the JsonSerializer APIs, applying reflection-based fallback where applica |
| | | 959 | | /// </summary> |
| | | 960 | | [RequiresUnreferencedCode(JsonSerializer.SerializationUnreferencedCodeMessage)] |
| | | 961 | | [RequiresDynamicCode(JsonSerializer.SerializationRequiresDynamicCodeMessage)] |
| | | 962 | | private void ConfigureForJsonSerializer() |
| | 682 | 963 | | { |
| | 682 | 964 | | if (JsonSerializer.IsReflectionEnabledByDefault) |
| | 682 | 965 | | { |
| | | 966 | | // Even if a resolver has already been specified, we need to root |
| | | 967 | | // the default resolver to gain access to the default converters. |
| | 682 | 968 | | DefaultJsonTypeInfoResolver defaultResolver = DefaultJsonTypeInfoResolver.DefaultInstance; |
| | | 969 | | |
| | 682 | 970 | | switch (_typeInfoResolver) |
| | | 971 | | { |
| | | 972 | | case null: |
| | | 973 | | // Use the default reflection-based resolver if no resolver has been specified. |
| | 682 | 974 | | _typeInfoResolver = defaultResolver; |
| | 682 | 975 | | break; |
| | | 976 | | |
| | 0 | 977 | | case JsonSerializerContext ctx when AppContextSwitchHelper.IsSourceGenReflectionFallbackEnabled: |
| | | 978 | | // .NET 6 compatibility mode: enable fallback to reflection metadata for JsonSerializerContext |
| | 0 | 979 | | _effectiveJsonTypeInfoResolver = JsonTypeInfoResolver.Combine(ctx, defaultResolver); |
| | | 980 | | |
| | 0 | 981 | | if (_cachingContext is { } cachingContext) |
| | 0 | 982 | | { |
| | | 983 | | // A cache has already been created by the source generator. |
| | | 984 | | // Repeat the same configuration routine for that options instance, if different. |
| | | 985 | | // Invalidate any cache entries that have already been stored. |
| | 0 | 986 | | if (cachingContext.Options != this && !cachingContext.Options._isConfiguredForJsonSerializer |
| | 0 | 987 | | { |
| | 0 | 988 | | cachingContext.Options.ConfigureForJsonSerializer(); |
| | 0 | 989 | | } |
| | | 990 | | else |
| | 0 | 991 | | { |
| | 0 | 992 | | cachingContext.Clear(); |
| | 0 | 993 | | } |
| | 0 | 994 | | } |
| | 0 | 995 | | break; |
| | | 996 | | } |
| | 682 | 997 | | } |
| | 0 | 998 | | else if (_typeInfoResolver is null or EmptyJsonTypeInfoResolver) |
| | 0 | 999 | | { |
| | 0 | 1000 | | ThrowHelper.ThrowInvalidOperationException_JsonSerializerIsReflectionDisabled(); |
| | | 1001 | | } |
| | | 1002 | | |
| | 682 | 1003 | | Debug.Assert(_typeInfoResolver != null); |
| | | 1004 | | // NB preserve write order. |
| | 682 | 1005 | | _isReadOnly = true; |
| | 682 | 1006 | | _isConfiguredForJsonSerializer = true; |
| | 682 | 1007 | | } |
| | | 1008 | | |
| | | 1009 | | /// <summary> |
| | | 1010 | | /// This flag is supplementary to <see cref="_isReadOnly"/> and is only used to keep track |
| | | 1011 | | /// of source-gen reflection fallback (assuming the IsSourceGenReflectionFallbackEnabled feature switch is on). |
| | | 1012 | | /// This mode necessitates running the <see cref="ConfigureForJsonSerializer"/> method even |
| | | 1013 | | /// for options instances that have been marked as read-only. |
| | | 1014 | | /// </summary> |
| | | 1015 | | private volatile bool _isConfiguredForJsonSerializer; |
| | | 1016 | | |
| | | 1017 | | // Only populated in .NET 6 compatibility mode encoding reflection fallback in source gen |
| | | 1018 | | private IJsonTypeInfoResolver? _effectiveJsonTypeInfoResolver; |
| | | 1019 | | |
| | | 1020 | | private JsonTypeInfo? GetTypeInfoNoCaching(Type type) |
| | 12254 | 1021 | | { |
| | 12254 | 1022 | | IJsonTypeInfoResolver? resolver = _effectiveJsonTypeInfoResolver ?? _typeInfoResolver; |
| | 12254 | 1023 | | if (resolver is null) |
| | 0 | 1024 | | { |
| | 0 | 1025 | | return null; |
| | | 1026 | | } |
| | | 1027 | | |
| | 12254 | 1028 | | JsonTypeInfo? info = resolver.GetTypeInfo(type, this); |
| | | 1029 | | |
| | 12254 | 1030 | | if (info != null) |
| | 12254 | 1031 | | { |
| | 12254 | 1032 | | if (info.Type != type) |
| | 0 | 1033 | | { |
| | 0 | 1034 | | ThrowHelper.ThrowInvalidOperationException_ResolverTypeNotCompatible(type, info.Type); |
| | | 1035 | | } |
| | | 1036 | | |
| | 12254 | 1037 | | if (info.Options != this) |
| | 0 | 1038 | | { |
| | 0 | 1039 | | ThrowHelper.ThrowInvalidOperationException_ResolverTypeInfoOptionsNotCompatible(); |
| | | 1040 | | } |
| | 12254 | 1041 | | } |
| | | 1042 | | else |
| | 0 | 1043 | | { |
| | 0 | 1044 | | Debug.Assert(_effectiveJsonTypeInfoResolver is null, "an effective resolver always returns metadata"); |
| | | 1045 | | |
| | 0 | 1046 | | if (type == JsonTypeInfo.ObjectType) |
| | 0 | 1047 | | { |
| | | 1048 | | // If the resolver does not provide a JsonTypeInfo<object> instance, fill |
| | | 1049 | | // with the serialization-only converter to enable polymorphic serialization. |
| | 0 | 1050 | | var converter = new SlimObjectConverter(resolver); |
| | 0 | 1051 | | info = new JsonTypeInfo<object>(converter, this); |
| | 0 | 1052 | | } |
| | 0 | 1053 | | } |
| | | 1054 | | |
| | 12254 | 1055 | | return info; |
| | 12254 | 1056 | | } |
| | | 1057 | | |
| | | 1058 | | internal JsonDocumentOptions GetDocumentOptions() |
| | 0 | 1059 | | { |
| | 0 | 1060 | | return new JsonDocumentOptions |
| | 0 | 1061 | | { |
| | 0 | 1062 | | AllowDuplicateProperties = AllowDuplicateProperties, |
| | 0 | 1063 | | AllowTrailingCommas = AllowTrailingCommas, |
| | 0 | 1064 | | CommentHandling = ReadCommentHandling, |
| | 0 | 1065 | | MaxDepth = MaxDepth, |
| | 0 | 1066 | | }; |
| | 0 | 1067 | | } |
| | | 1068 | | |
| | | 1069 | | internal JsonNodeOptions GetNodeOptions() |
| | 1290 | 1070 | | { |
| | 1290 | 1071 | | return new JsonNodeOptions |
| | 1290 | 1072 | | { |
| | 1290 | 1073 | | PropertyNameCaseInsensitive = PropertyNameCaseInsensitive |
| | 1290 | 1074 | | }; |
| | 1290 | 1075 | | } |
| | | 1076 | | |
| | | 1077 | | internal JsonReaderOptions GetReaderOptions() |
| | 55924 | 1078 | | { |
| | 55924 | 1079 | | return new JsonReaderOptions |
| | 55924 | 1080 | | { |
| | 55924 | 1081 | | AllowTrailingCommas = AllowTrailingCommas, |
| | 55924 | 1082 | | CommentHandling = ReadCommentHandling, |
| | 55924 | 1083 | | MaxDepth = EffectiveMaxDepth |
| | 55924 | 1084 | | }; |
| | 55924 | 1085 | | } |
| | | 1086 | | |
| | | 1087 | | internal JsonWriterOptions GetWriterOptions() |
| | 0 | 1088 | | { |
| | 0 | 1089 | | return new JsonWriterOptions |
| | 0 | 1090 | | { |
| | 0 | 1091 | | Encoder = Encoder, |
| | 0 | 1092 | | Indented = WriteIndented, |
| | 0 | 1093 | | IndentCharacter = IndentCharacter, |
| | 0 | 1094 | | IndentSize = IndentSize, |
| | 0 | 1095 | | MaxDepth = EffectiveMaxDepth, |
| | 0 | 1096 | | NewLine = NewLine, |
| | 0 | 1097 | | #if !DEBUG |
| | 0 | 1098 | | SkipValidation = true |
| | 0 | 1099 | | #endif |
| | 0 | 1100 | | }; |
| | 0 | 1101 | | } |
| | | 1102 | | |
| | | 1103 | | internal void VerifyMutable() |
| | 2728 | 1104 | | { |
| | 2728 | 1105 | | if (_isReadOnly) |
| | 0 | 1106 | | { |
| | 0 | 1107 | | ThrowHelper.ThrowInvalidOperationException_SerializerOptionsReadOnly(_typeInfoResolver as JsonSerializer |
| | | 1108 | | } |
| | 2728 | 1109 | | } |
| | | 1110 | | |
| | | 1111 | | private sealed class ConverterList : ConfigurationList<JsonConverter> |
| | | 1112 | | { |
| | | 1113 | | private readonly JsonSerializerOptions _options; |
| | | 1114 | | |
| | | 1115 | | public ConverterList(JsonSerializerOptions options, IList<JsonConverter>? source = null) |
| | 0 | 1116 | | : base(source) |
| | 0 | 1117 | | { |
| | 0 | 1118 | | _options = options; |
| | 0 | 1119 | | } |
| | | 1120 | | |
| | 0 | 1121 | | public override bool IsReadOnly => _options.IsReadOnly; |
| | 0 | 1122 | | protected override void OnCollectionModifying() => _options.VerifyMutable(); |
| | | 1123 | | } |
| | | 1124 | | |
| | | 1125 | | private sealed class OptionsBoundJsonTypeInfoResolverChain : JsonTypeInfoResolverChain |
| | | 1126 | | { |
| | | 1127 | | private JsonSerializerOptions? _options; |
| | | 1128 | | |
| | 0 | 1129 | | public OptionsBoundJsonTypeInfoResolverChain(JsonSerializerOptions options) |
| | 0 | 1130 | | { |
| | 0 | 1131 | | _options = options; |
| | 0 | 1132 | | AddFlattened(options._typeInfoResolver); |
| | 0 | 1133 | | } |
| | | 1134 | | |
| | | 1135 | | public void DetachFromOptions() |
| | 0 | 1136 | | { |
| | 0 | 1137 | | _options = null; |
| | 0 | 1138 | | } |
| | | 1139 | | |
| | 0 | 1140 | | public override bool IsReadOnly => _options?.IsReadOnly is true; |
| | | 1141 | | |
| | | 1142 | | protected override void ValidateAddedValue(IJsonTypeInfoResolver item) |
| | 0 | 1143 | | { |
| | 0 | 1144 | | Debug.Assert(item is not null); |
| | | 1145 | | |
| | 0 | 1146 | | if (ReferenceEquals(item, this) || ReferenceEquals(item, _options?._typeInfoResolver)) |
| | 0 | 1147 | | { |
| | | 1148 | | // Cannot add the instances in TypeInfoResolver or TypeInfoResolverChain to the chain itself. |
| | 0 | 1149 | | ThrowHelper.ThrowInvalidOperationException_InvalidChainedResolver(); |
| | 0 | 1150 | | } |
| | 0 | 1151 | | } |
| | | 1152 | | |
| | | 1153 | | protected override void OnCollectionModifying() |
| | 0 | 1154 | | { |
| | 0 | 1155 | | _options?.VerifyMutable(); |
| | 0 | 1156 | | } |
| | | 1157 | | |
| | | 1158 | | protected override void OnCollectionModified() |
| | 0 | 1159 | | { |
| | | 1160 | | // Collection modified by the user: replace the main |
| | | 1161 | | // resolver with the resolver chain as our source of truth. |
| | 0 | 1162 | | _options?._typeInfoResolver = this; |
| | 0 | 1163 | | } |
| | | 1164 | | } |
| | | 1165 | | |
| | | 1166 | | [RequiresUnreferencedCode(JsonSerializer.SerializationUnreferencedCodeMessage)] |
| | | 1167 | | [RequiresDynamicCode(JsonSerializer.SerializationRequiresDynamicCodeMessage)] |
| | | 1168 | | private static JsonSerializerOptions GetOrCreateSingleton( |
| | | 1169 | | ref JsonSerializerOptions? location, |
| | | 1170 | | JsonSerializerDefaults defaults) |
| | 0 | 1171 | | { |
| | 0 | 1172 | | var options = new JsonSerializerOptions(defaults) |
| | 0 | 1173 | | { |
| | 0 | 1174 | | // Because we're marking the default instance as read-only, |
| | 0 | 1175 | | // we need to specify a resolver instance for the case where |
| | 0 | 1176 | | // reflection is disabled by default: use one that returns null for all types. |
| | 0 | 1177 | | |
| | 0 | 1178 | | TypeInfoResolver = JsonSerializer.IsReflectionEnabledByDefault |
| | 0 | 1179 | | ? DefaultJsonTypeInfoResolver.DefaultInstance |
| | 0 | 1180 | | : JsonTypeInfoResolver.Empty, |
| | 0 | 1181 | | |
| | 0 | 1182 | | _isReadOnly = true, |
| | 0 | 1183 | | }; |
| | | 1184 | | |
| | 0 | 1185 | | return Interlocked.CompareExchange(ref location, options, null) ?? options; |
| | 0 | 1186 | | } |
| | | 1187 | | |
| | | 1188 | | [DebuggerBrowsable(DebuggerBrowsableState.Never)] |
| | 0 | 1189 | | private string DebuggerDisplay => $"TypeInfoResolver = {(TypeInfoResolver?.ToString() ?? "<null>")}, IsReadOnly |
| | | 1190 | | } |
| | | 1191 | | } |