-
Notifications
You must be signed in to change notification settings - Fork 5.2k
Description
The JSON source generator (#45448) takes a metadata-based approach where reflection-based type metadata gathering is moved from run-time to compile time. This primarily helps improve start-up time, privates bytes usage, and app size.
For simple JsonSerializerOptions usages, we can also generate serialization logic using Utf8JsonWriter directly. which can help improve serialization throughput.
API Proposal
namespace System.Text.Json.Serialization.Metadata
{
public abstract partial class JsonTypeInfo<T>
{
// Existing:
// internal JsonTypeInfo() { }
// A method that can be called to serialize an instance of T, using <see cref="JsonSerializerOptions"/> specified at compile-time.
public Action<Utf8JsonWriter, T>? Serialize { get; set; }
}
// Instructs the System.Text.Json source generator to assume the specified options will be used at run-time via <see cref="JsonSerializerOptions"/>.
[AttributeUsage(AttributeTargets.Assembly, AllowMultiple = false)]
public class JsonSerializerOptionsAttribute : JsonAttribute
{
// Specifies the default ignore condition.
public JsonIgnoreCondition DefaultIgnoreCondition { get; set; }
// Specifies whether to ignore read-only fields.
public bool IgnoreReadOnlyFields { get; set; }
// Specifies whether to ignore read-only properties.
public bool IgnoreReadOnlyProperties { get; set; }
// Specifies whether to ignore custom converters provided at run-time.
public bool IgnoreRuntimeCustomConverters { get; set; }
// Specifies whether to include fields for serialization and deserialization.
public bool IncludeFields { get; set; }
// Specifies a built-in naming polices to convert JSON property names with.
public JsonKnownNamingPolicy NamingPolicy { get; set; }
// Specifies whether JSON output should be pretty-printed.
public bool WriteIndented { get; set; }
}
}
namespace System.Text.Json.Serialization
{
// Existing: [AttributeUsage(AttributeTargets.Assembly, AllowMultiple = true)]
[AttributeUsage(AttributeTargets.Assembly | AttributeTargets.Class | AttributeTargets.Struct | AttributeTargets.Interface, AllowMultiple = true)]
public sealed class JsonSerializableAttribute : Attribute
{
// Existing:
// public string TypeInfoPropertyName { get; set; }
// public JsonSerializableAttribute(Type type) { }
// Constructor to be place directly on types.
public JsonSerializableAttribute() { }
// Instructs the generator on what to generate for the type.
public JsonSourceGenerationMode GenerationMode { get; set; }
}
public enum JsonKnownNamingPolicy
{
Unspecified = 0,
BuiltInCamelCase = 1
}
// The mode for source generation by the System.Text.Json source generator.
public enum JsonSourceGenerationMode
{
// Instructs the JSON source generator to generate serialization logic
// and type metadata to fallback to when the run-time options don't match.
MetadataAndSerialization = 0,
// Instructs the JSON source generator to generate type-metadata initialization logic.
Metadata = 1,
// Instructs the JSON source generator to generate serialization logic.
Serialization = 2,
}
}Feature behavior
- Fast-path methods are only overriden when
[JsonSerializerOptionsAttribute]is used. - The generated
JsonContext.Defaultproperty will generate/use an options populated with the values from the[JsonSerializerOptionsAttribute].
Scenarios
Given a simple type:
[JsonSerializable]
public struct JsonMessage
{
public string Message { get; set; }
}Calling fast-path directly
[assembly: JsonSerializerOptions(NamingPolicy = JsonKnownNamingPolicy.BuiltInCamelCase)]
JsonTypeInfo<JsonMessage> messageInfo = JsonContext.Default.JsonMessage;
var ms = new MemoryStream();
using (var writer = new Utf8JsonWriter(ms))
{
messageInfo.Serialize!(writer, message);
}Using fast-path via JsonSerializer
Some features like reference-loop handling & async (de)serialization are not supported in generated fast-path logic. For those cases, the context or type info should be passed to the serializer directly. The serializer will detect when the fast path can be called or not. For example for reference-handling, the serializer would know that it can call the fast-path logic for primitives and structs.
[assembly: JsonSerializerOptions]
JsonContext context = new JsonContext(new JsonSerializerOptions() { ReferenceHandler = ReferenceHandler.Preserve });
JsonSerializer.Serialize(new JsonMessage(), JsonContext.Default.JsonMessage);