Skip to content
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

Merged
merged 8 commits into from
Aug 24, 2021

Conversation

tdykstra
Copy link
Contributor

@tdykstra tdykstra commented Aug 3, 2021

Fixes #25032

File Type File Name Published Url
Content docs/standard/serialization/system-text-json--source-generation.md https://review.docs.microsoft.com/en-us/dotnet/standard/serialization/system-text-json-source-generation?branch=main

Copy link
Member

@BillWagner BillWagner left a 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 :shipit:

@tdykstra tdykstra merged commit bb8b554 into dotnet:main Aug 24, 2021
@tdykstra tdykstra deleted the stjsourcegen branch August 24, 2021 21:53
@tdykstra tdykstra added the okr-freshness OKR: Freshness of content label Sep 14, 2021
Copy link
Contributor

@layomia layomia left a 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.
Copy link
Contributor

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>.
Copy link
Contributor

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*.
Copy link
Contributor

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.
Copy link
Contributor

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.
Copy link
Contributor

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.
Copy link
Contributor

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`
Copy link
Contributor

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.
Copy link
Contributor

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.
Copy link
Contributor

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.

Copy link
Contributor

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 by JsonSourceGenerationOptionsAttribute.

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)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Document source generation for System.Text.Json
4 participants