-
Notifications
You must be signed in to change notification settings - Fork 5.2k
Description
This is the API proposal and feature set for object (de)serialization. It covers the main API entry point (JsonSerializer
), the options (JsonSerializerOptions
) and the ability to ignore properties during (de)serialization.
Review process update: due to the size of this issue and since new API issues are being added for new features, the overview and forward-looking information has been moved to https://github.com/dotnet/corefx/blob/master/src/System.Text.Json/docs/SerializerProgrammingModel.md. Future API additions will have their own independent API issue created instead of re-using this issue.
Current Status
The reviewed portion of the API is in corefx master and Preview 4. It consists of the JsonSerializer
and the JsonSerializerOptions
class and provides no extensibility features.
There is a previous prototype at CoreFxLab in the package System.Text.JsonLab.Serialization
. It contains non-reviewed APIs including extensibility features like using attributes to define custom value converters, property name policies (like camel-casing), etc. It is build against .NET Core 3.0 Preview 2.
The last status of the API review is shown below.
API
JsonSerializer
This static class is the main entry point.
Let's start with coding examples before the formal API is provided.
Using a simple POCO class:
public class Person
{
public string FirstName { get; set; }
public string LastName { get; set; }
public DateTime? BirthDay { get; set; }
}
To deserialize JSON bytes into a POCO instance:
ReadOnlySpan<byte> utf8= ...
Person person = JsonSerializer.Parse<Person>(utf8);
To serialize an object to JSON bytes:
Person person = ...
byte[] utf8 = JsonSerializer.ToBytes(person);
The string-based Parse()
and ToString()
are convenience methods for strings, but slower than using the <byte>
flavors because UTF8 must be converted to\from UTF16.
namespace System.Text.Json.Serialization
{
public static class JsonSerializer
{
public static object Parse(ReadOnlySpan<byte> utf8Json, Type returnType, JsonSerializerOptions options = null);
public static TValue Parse<TValue>(ReadOnlySpan<byte> utf8Json, JsonSerializerOptions options = null);
public static object Parse(string json, Type returnType, JsonSerializerOptions options = null);
public static TValue Parse<TValue>(string json, JsonSerializerOptions options = null);
public static ValueTask<object> ReadAsync(Stream utf8Json, Type returnType, JsonSerializerOptions options = null, CancellationToken cancellationToken = default(CancellationToken));
public static ValueTask<TValue> ReadAsync<TValue>(Stream utf8Json, JsonSerializerOptions options = null, CancellationToken cancellationToken = default(CancellationToken));
public static byte[] ToBytes(object value, Type type, JsonSerializerOptions options = null);
public static byte[] ToBytes<TValue>(TValue value, JsonSerializerOptions options = null);
public static string ToString(object value, Type type, JsonSerializerOptions options = null);
public static string ToString<TValue>(TValue value, JsonSerializerOptions options = null);
public static Task WriteAsync(object value, Type type, Stream utf8Json, JsonSerializerOptions options = null, CancellationToken cancellationToken = default(CancellationToken));
public static Task WriteAsync<TValue>(TValue value, Stream utf8Json, JsonSerializerOptions options = null, CancellationToken cancellationToken = default(CancellationToken));
}
}
JsonSerializerOptions
This class contains the options that are used during (de)serialization.
If an instance of JsonSerializerOptions
is not specified when calling read\write then a default instance is used which is immutable and private. Having a global\static instance is not a viable feature because of unintended side effects when more than one area of code changes the same settings. Having a instance specified per thread\context mechanism is possible, but will only be added pending feedback. It is expected that ASP.NET and other consumers that have non-default settings maintain their own global, thread or stack variable and pass that in on every call. ASP.NET and others may also want to read a .config file at startup in order to initialize the options instance.
An instance of this class and exposed objects will be immutable once (de)serialization has occurred. This allows the instance to be shared globally with the same settings without the worry of side effects. The immutability is also desired with a future code-generation feature. Due to the immutable nature and fine-grain control over options through Attributes, it is expected that the instance is shared across users and applications. We may provide a Clone() method pending feedback.
For performance, when a JsonSerializerOptions
instance is used, it should be cached or re-used especially when run-time attributes are added because when that occurs, caches are held by the instance instead of being global.
namespace System.Text.Json.Serialization
{
/// <summary>
/// Provides options to be used with <see cref="JsonSerializer"/>.
/// </summary>
public class JsonSerializerOptions
{
public JsonSerializerOptions();
/// <summary>
/// Defines whether an extra comma at the end of a list of JSON values in an object or array
/// are allowed (and ignored) within the JSON payload being read.
/// By default, it's set to false, and the reader will throw a <exception cref="JsonReaderException"/> if it encounters a trailing comma.
/// </summary>
/// <exception cref="InvalidOperationException">
/// Thrown if this property is set after serialization or deserialization has occurred.
/// </exception>
public bool AllowTrailingCommas {get; set;}
/// <summary>
/// The default buffer size in bytes used when creating temporary buffers.
/// </summary>
/// <remarks>The default size is 16K.</remarks>
/// <exception cref="System.ArgumentException">Thrown when the buffer size is less than 1.</exception>
/// <exception cref="InvalidOperationException">
/// Thrown if this property is set after serialization or deserialization has occurred.
/// </exception>
public int DefaultBufferSize { get; set; }
/// <summary>
/// Determines whether null values are ignored during serialization and deserialization.
/// The default value is false.
/// </summary>
/// <exception cref="InvalidOperationException">
/// Thrown if this property is set after serialization or deserialization has occurred.
/// </exception>
public bool IgnoreNullValues { get; set; }
/// <summary>
/// Determines whether readonly properties are ignored during serialization and deserialization.
/// A property is readonly if it contains a public getter but not a public setter.
/// The default value is false.
/// </summary>
/// <exception cref="InvalidOperationException">
/// Thrown if this property is set after serialization or deserialization has occurred.
/// </exception>
public bool IgnoreReadOnlyProperties { get; set; }
/// <summary>
/// Gets or sets the maximum depth allowed when reading or writing JSON, with the default (i.e. 0) indicating a max depth of 64.
/// Reading past this depth will throw a <exception cref="JsonReaderException"/>.
/// </summary>
/// <exception cref="InvalidOperationException">
/// Thrown if this property is set after serialization or deserialization has occurred.
/// </exception>
public int MaxDepth { get; set; }
/// <summary>
/// Defines how the <see cref="Utf8JsonReader"/> should handle comments when reading through the JSON.
/// By default the reader will throw a <exception cref="JsonReaderException"/> if it encounters a comment.
/// </summary>
/// <exception cref="InvalidOperationException">
/// Thrown if this property is set after serialization or deserialization has occurred.
/// </exception>
public JsonCommentHandling ReadCommentHandling { get; set; }
/// <summary>
/// Defines whether the <see cref="Utf8JsonWriter"/> should pretty print the JSON which includes:
/// indenting nested JSON tokens, adding new lines, and adding white space between property names and values.
/// By default, the JSON is written without any extra white space.
/// </summary>
/// <exception cref="InvalidOperationException">
/// Thrown if this property is set after serialization or deserialization has occurred.
/// </exception>
public bool WriteIndented { get; set; }
// several more options here for other features not covered here
}
/// <summary>
/// The base class of serialization attributes.
/// </summary>
public abstract class JsonAttribute : Attribute
{
}
/// <summary>
/// Prevents a property from being serialized or deserialized.
/// </summary>
[AttributeUsage(AttributeTargets.Property, AllowMultiple = false)]
public sealed class JsonIgnoreAttribute : JsonAttribute
{
public JsonIgnoreAttribute();
}
}