Source-generation modes in System.Text.Json

Source generation can be used in two modes: metadata-based and serialization optimization. This article describes the different modes.

For information about how to use source generation modes, see How to use source generation in System.Text.Json.

Metadata-based mode

You can use source generation to move the metadata collection process from run time to compile time. During compilation, the metadata is collected and source code files are generated. The generated source code files are automatically compiled as an integral part of the application. This technique eliminates run-time metadata collection, which improves performance of both serialization and deserialization.

The performance improvements provided by source generation can be substantial. For example, test results have shown up to 40% or more startup time reduction, private memory reduction, throughput speed increase (in serialization optimization mode), and app size reduction.

Known issues

Only public properties and fields are supported by default in either serialization mode. However, reflection mode supports the use of private accessors, while source-generation mode doesn't. For example, you can apply the JsonInclude attribute to a property that has a private setter or getter and it will be serialized in reflection mode. Source-generation mode supports only public or internal accessors of public properties. If you set [JsonInclude] on non-public accessors and choose source-generation mode, a NotSupportedException will be thrown at run time.

Only public properties and fields are supported by default in either serialization mode. However, reflection mode supports the use of private members and private accessors, while source-generation mode doesn't. For example, if you apply the JsonInclude attribute to a private property or a property that has a private setter or getter, it will be serialized in reflection mode. Source-generation mode supports only public or internal members and public or internal accessors of public properties. If you set [JsonInclude] on private members or accessors and choose source-generation mode, a NotSupportedException will be thrown at run time.

For information about other known issues with source generation, see the GitHub issues that are labeled "source-generator" in the dotnet/runtime repository.

Serialization-optimization (fast path) mode

JsonSerializer has many features that customize the output of serialization, such as naming policies and preserving references. Support for all those features causes some performance overhead. Source generation can improve serialization performance by generating optimized code that uses Utf8JsonWriter directly.

The optimized code doesn't support all of the serialization features that JsonSerializer supports. The serializer detects whether the optimized code can be used and falls back to default serialization code if unsupported options are specified. For example, JsonNumberHandling.AllowReadingFromString isn't applicable to writing, so specifying this option doesn't cause a fallback to default code.

The following table shows which options in JsonSerializerOptions are supported by fast-path serialization:

Serialization option Supported for fast-path
AllowTrailingCommas ✔️
Converters
DefaultBufferSize ✔️
DefaultIgnoreCondition ✔️
DictionaryKeyPolicy
Encoder
IgnoreNullValues
IgnoreReadOnlyFields ✔️
IgnoreReadOnlyProperties ✔️
IncludeFields ✔️
MaxDepth ✔️
NumberHandling
PropertyNamingPolicy ✔️
ReferenceHandler
TypeInfoResolver ✔️
WriteIndented ✔️

(The following options aren't supported because they apply only to deserialization: PropertyNameCaseInsensitive, ReadCommentHandling, and UnknownTypeHandling.)

The following table shows which attributes are supported by fast-path serialization:

Attribute Supported for fast-path
JsonConstructorAttribute
JsonConverterAttribute
JsonDerivedTypeAttribute ✔️
JsonExtensionDataAttribute
JsonIgnoreAttribute ✔️
JsonIncludeAttribute ✔️
JsonNumberHandlingAttribute
JsonPolymorphicAttribute ✔️
JsonPropertyNameAttribute ✔️
JsonPropertyOrderAttribute ✔️
JsonRequiredAttribute ✔️

If a non-supported option or attribute is specified for a type, the serializer falls back to metadata mode, assuming that the source generator has been configured to generate metadata. In that case, the optimized code isn't used when serializing that type but may be used for other types. Therefore it's important to do performance testing with your options and workloads to determine how much benefit you can actually get from serialization-optimization mode. Also, the ability to fall back to JsonSerializer code requires metadata-collection mode. If you select only serialization-optimization mode, serialization might fail for types or options that need to fall back to JsonSerializer code.

See also