Overview

JSON serialization/deserialization behavior can be controlled through JsonSerializerOptions and various attributes.

Serialization Behavior

By default:

  • Public properties are serialized
  • Fields are ignored
  • Casing of JSON names matches the .NET names

Deserialization Behavior

By default:

  • Property name matching is case sensitive
  • Read-only properties are ignored (no value is deserialized into readonly properties)
  • Fields are ignored
  • Non-public constructors are ignored
  • Enums are supported as numbers

JsonSerializerOptions Web Defaults

The defaults in ASP.NET Core apps are different than .NET apps. See: https://learn.microsoft.com/en-us/dotnet/standard/serialization/system-text-json/configure-options#web-defaults-for-jsonserializeroptions

Guidance for Using JsonSerializerOptions

  1. Caution: Do not create a new instance of JsonSerializerOptions each time you use it.
  2. If you need a JsonSerializerOptions instance that has all default options, use the JsonSerializerOptions.Default property.

Overriding Default Behavior

Properties

Desired BehaviorApproachNotes
Use case-insensitive property namesUse JsonSerializerOptions.PropertyNameCaseInsensitive = trueN/A
Customize individual property namesUse the [JsonPropertyName("NAME")] attributeN/A
Use camel case property namesSet JsonSerializerOptions’s PropertyNamingPolicy property to JsonNamingPolicy.CamelCaseThe [JsonPropertyName] attribute takes precedence and will override JsonNamingPolicy
Configure the order of serialized propertiesUse [JsonPropertyOrder(N)] attribute where N is an integer specifying orderN/A
Ignore individual propertiesUse the [JsonIgnore] attribute on the properties to ignoreThis attribute has an optional Condition property: [JsonIgnore(Condition = JsonIgnoreCondition.CONDITION)]
Ignore all read-only propertiesUse JsonSerializerOptions’s IgnoreReadOnlyProperties propertyN/A
Ignore all null-value propertiesSet JsonSerializerOptions DefaultIgnoreCondition property to JsonIgnoreCondition.WhenWritingNullN/A
Ignore all default-value propertiesSet JsonSerializerOptions DefaultIgnoreCondition property to JsonIgnoreCondition.WhenWritingDefaultN/A

Fields

Desired BehaviorApproachNotes
Include fieldsUse the [JsonInclude] attribute on the fieldsAlternatively, use JsonSerializerOptions.IncludeFields
Ignore read-only fields when serializingUse JsonSerializerOptions’s IgnoreReadOnlyFieldsN/A

Marking Properties or Fields Required for JSON Deserialization

ℹ️ Important

Availability: C# 11

There are three approaches:

  1. Via the required modifier
    using System.Text.Json;
    
    // The following line throws a JsonException at run time.
    Console.WriteLine(JsonSerializer.Deserialize<Person>("""{"Age": 42}"""));
    
    public class Person
    {
        public required string Name { get; set; }
        public int Age { get; set; }
    }
    
  2. Via the [JsonRequiredAttribute]:
    using System.Text.Json;
    
    // The following line throws a JsonException at run time.
    Console.WriteLine(JsonSerializer.Deserialize<Person>("""{"Age": 42}"""));
    
    public class Person
    {
        [JsonRequired]
        public string Name { get; set; }
        public int Age { get; set; }
    }
    
  3. Via the JsonPropertyInfo.IsRequired property of the contract model:
    var options = new JsonSerializerOptions
    {
        TypeInfoResolver = new DefaultJsonTypeInfoResolver
        {
            Modifiers =
            {
                static typeInfo =>
                {
                    if (typeInfo.Kind != JsonTypeInfoKind.Object)
                        return;
    
                    foreach (JsonPropertyInfo propertyInfo in typeInfo.Properties)
                    {
                        // Strip IsRequired constraint from every property.
                        propertyInfo.IsRequired = false;
                    }
                }
            }
        }
    };
    
    // Deserialization now succeeds even though the Name property isn't in the JSON payload.
    JsonSerializer.Deserialize<Person>("""{"Age": 42}""", options);
    

Overriding Other Behavior

Desired BehaviorApproachNotes
Use camel case dictionary keysSet JsonSerializerOptions’s DictionaryKeyPolicy property to JsonNamingPolicy.CamelCaseApplies to serialization only
Use enums as stringsSet JsonSerializerOptions’s Converters property with a new JsonStringEnumConverter(JsonNamingPolicy.CamelCase)N/A
Allow comments in JSONSet JsonSerializerOptions’s ReadCommentHandling property to JsonCommentHandling.SkipN/A
Allow trailing commas in JSONSet JsonSerializerOptions’s AllowTrailingCommas property to trueN/A
Serialize numbers in quotesSet JsonSerializerOptions’s NumberHandling property to JsonNumberHandling.AllowReadingFromStringN/A
Deserialize numbers in quotesSet JsonSerializerOptions’s NumberHandling property to JsonNumberHandling.WriteAsStringN/A
Handle overflow JSONSee this pageN/A
Handle references and circular referencesSee this pageN/A
Deserialize to immutable typesSee this pageN/A

All built-in naming policies

ℹ️ Important

Availability: .NET 8

As of .NET 8, JsonNamingPolicy includes 4 more built-in naming policies:

  • KebabCaseLower
  • KebabCaseUpper
  • SnakeCaseLower
  • SnakeCaseUpper