-
Notifications
You must be signed in to change notification settings - Fork 5.9k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Document source generation for System.Text.Json #25451
Conversation
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is really great @tdykstra
I'd not heard of this feature, and after reading these, I'm confident I can use it effectively now.
There are a few warnings on snippet tags, and then this is ready to
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Great article! Just have a few comments to be considered.
> [!IMPORTANT] | ||
> Some information relates to prerelease product that may be substantially modified before it's released. Microsoft makes no warranties, express or implied, with respect to the information provided here. | ||
|
||
[System.Text.Json](system-text-json-overview.md) can use the C# [source generation](../../csharp/roslyn-sdk/source-generators-overview.md) feature to improve performance, reduce private memory usage, and improve [assembly trimming](../../core/deploying/trim-self-contained.md) accuracy. This article shows how to use the source generation features of System.Text.Json. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
reduce private memory usage, and facilitate* [assembly trimming] which reduces app size.
or something like that
| <xref:System.Text.Json.JsonSerializerOptions.ReferenceHandler> | ❌ | | ||
| <xref:System.Text.Json.JsonSerializerOptions.WriteIndented> \* | ✔️ | | ||
|
||
\* Some `Serialize` methods let you pass in a `Utf8JsonWriter` instance. In that case, you can set the `Encoder` and `WriteIndented` options by using <xref:System.Text.Json.JsonWriterOptions.Indented?displayProperty=nameWithType> and <xref:System.Text.Json.JsonWriterOptions.Indented?displayProperty=nameWithType>. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't think this note is needed. We might have an issue with mismatches on writer/serializer encoder - dotnet/runtime#59424 & we don't want to exacerbate them.
|
||
### Metadata collection mode | ||
|
||
To serialize or deserialize a type, <xref:System.Text.Json.JsonSerializer> needs information about how to access the members of the type. To serialize, `JsonSerializer` needs to know how to access property getters and fields. To deserialize, it needs to know how to access a constructor, property setters, and fields. It also needs to know if any attributes have been used to customize serialization or deserialization. This information is referred to as *metadata*. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Metadata includes runtime config from JsonSerializerOptions - we should include that.
|
||
### Serialization optimization mode | ||
|
||
`JsonSerializer` has many features that customize the output of serialization, such as [camel-casing property names](system-text-json-customize-properties.md#use-camel-case-for-all-json-property-names) and [preserving references](system-text-json-preserve-references.md#preserve-references-and-handle-circular-references). Support for all those features causes some performance overhead. Source generation can improve serialization performance by generating optimized code that uses [`Utf8JsonWriter`](system-text-json-use-dom-utf8jsonreader-utf8jsonwriter.md#use-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. For example, reference-handling isn't supported but even if it's selected, the serializer knows that it can call the optimized code for primitives and structs. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
For example, reference-handling isn't supported but even if it's selected, the serializer knows that it can call the optimized code for primitives and structs.
We eventually want to have this level of sophistication, but for now, if any reference handling feature is specified on the runtime options, then we fallback to serializer logic. A better example is when JsonNumberHandling.AllowReadingFromString
is specified: because this is not applicable to writing, we proceed with fast-path logic assuming other logic allows.
|
||
The performance improvements can be substantial. For example, [test results](https://devblogs.microsoft.com/dotnet/try-the-new-system-text-json-source-generator/#how-source-generation-provides-benefits) have shown up to 40% or more startup time reduction, private memory reduction, throughput speed increase, and app size reduction. | ||
|
||
\* As explained in the [preceding section](#serialization-optimization-mode), the optimized code doesn't support some features. 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 that need to fall back to `JsonSerializer` code. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
serialization might fail for types *or options that need to fall back to JsonSerializer
code.
|
||
* Create a partial class that derives from <xref:System.Text.Json.Serialization.JsonSerializerContext>. | ||
* Specify the type to serialize or deserialize by applying <xref:System.Text.Json.Serialization.JsonSerializableAttribute> to the context class. | ||
* Pass an instance of the context class to a <xref:System.Text.Json.JsonSerializer> method. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The recommended approach is to use strongly typed implementations of the serializer: i.e an overload that takes a JsonTypeInfo<T>
instance, rather than one that takes a context instance. The below examples should use typed implementations & the "JsonSerializer methods" section should list them first.
|
||
In the preceding examples, the static `Default` property of the context type provides an instance of the context type with default options. The context instance provides a `WeatherForecast` property that returns a `JsonTypeInfo<WeatherForecast>` instance. You can specify a different name for this property by using the <xref:System.Text.Json.Serialization.JsonSerializableAttribute.TypeInfoPropertyName> property of the `[JsonSerializable]` attribute. | ||
|
||
### `JsonTypeInfo<TValue>.Serialize` |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This property has been renamed to JsonTypeInfo<TValue>.SerializeHandler
, and is now considered a lower level API that should only be called by source generated code. This is mostly because it might be difficult to reason about when it is null
or not (i.e. recalling the earlier table of what's supported in fast-path mode). I suggest to remove this section. Similarly, code samples calling this method directly should be refactored to instead go through the serializer.
|
||
## Specify source generation mode | ||
|
||
You can specify metadata collection mode or serialization optimization mode for an entire context, which may include multiple types. Or you can specify the mode for a single type. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Might go without saying, but we could mention that the setting for a single types wins over the setting for the entire context.
|
||
If you call a method that lets you pass in your own instance of `Utf8JsonWriter`, the writer's <xref:System.Text.Json.JsonWriterOptions.Indented> setting is honored instead of the `JsonSourceGenerationOptionsAttribute.WriteIndented` option. | ||
|
||
If you create and use a context instance by calling its constructor, a `JsonSerializerOptions` instance will be used instead of the options specified by `JsonSourceGenerationOptionsAttribute`. If you call the parameterless constructor of the context type, a `JsonSerializerOptions` instance with default options is created. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If you call the parameterless constructor of the context type, a
JsonSerializerOptions
instance with default options is created.
Let's not should advertise this parameterless ctor. It's mostly an indirect helper for the JsonSerializerOptions.AddContext
method.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If you create and use a context instance by calling its constructor, a
JsonSerializerOptions
instance will be used instead of the options specified byJsonSourceGenerationOptionsAttribute
.
nit, change to: If you create and use a context instance by calling the constructor that takes a JsonSerializerOptions
instance, the supplied instance will be used instead of the options specified by JsonSourceGenerationOptionsAttribute
. (or some other phrasing)
Fixes #25032