diff --git a/docs/standard/serialization/snippets/system-text-json-how-to-5-0/csharp/CopyOptions.cs b/docs/standard/serialization/snippets/system-text-json-how-to-5-0/csharp/CopyOptions.cs new file mode 100644 index 0000000000000..5566c16f5a71b --- /dev/null +++ b/docs/standard/serialization/snippets/system-text-json-how-to-5-0/csharp/CopyOptions.cs @@ -0,0 +1,44 @@ +using System; +using System.Text.Json; + +namespace CopyOptions +{ + public class Forecast + { + public DateTime Date { get; init; } + public int TemperatureC { get; set; } + public string Summary { get; set; } + }; + + public class Program + { + public static void Main() + { + Forecast forecast = new() + { + Date = DateTime.Now, + TemperatureC = 40, + Summary = "Hot" + }; + + JsonSerializerOptions options = new() + { + WriteIndented = true + }; + + JsonSerializerOptions optionsCopy = new(options); + string forecastJson = JsonSerializer.Serialize + (forecast, optionsCopy); + Console.WriteLine($"Output JSON:\n{forecastJson}"); + } + } +} + +// Produces output like the following example: +// +//Output JSON: +//{ +// "Date": "2020-10-21T15:40:06.8998502-07:00", +// "TemperatureC": 40, +// "Summary": "Hot" +//} diff --git a/docs/standard/serialization/snippets/system-text-json-how-to-5-0/csharp/CustomConverterHandleNull.cs b/docs/standard/serialization/snippets/system-text-json-how-to-5-0/csharp/CustomConverterHandleNull.cs new file mode 100644 index 0000000000000..0c5d39dccaba7 --- /dev/null +++ b/docs/standard/serialization/snippets/system-text-json-how-to-5-0/csharp/CustomConverterHandleNull.cs @@ -0,0 +1,53 @@ +using System; +using System.Collections.Generic; +using System.Text.Json; +using System.Text.Json.Serialization; + +namespace CustomConverterHandleNull +{ + public class Point + { + public int X { get; set; } + public int Y { get; set; } + + [JsonConverter(typeof(DescriptionConverter))] + public string Description { get; set; } + } + + public class DescriptionConverter : JsonConverter + { + public override bool HandleNull => true; + + public override string Read( + ref Utf8JsonReader reader, + Type typeToConvert, + JsonSerializerOptions options) + { + string val = reader.GetString(); + return val ?? "No description provided."; + } + + public override void Write( + Utf8JsonWriter writer, + string value, + JsonSerializerOptions options) + { + writer.WriteStringValue(value); + } + } + + public class Program + { + public static void Main() + { + string json = "{\"x\":1,\"y\":2,\"Description\":null}"; + + Point point = JsonSerializer.Deserialize(json); + Console.WriteLine($"Description: {point.Description}"); + } + } +} + +// Produces output like the following example: +// +//Description: No description provided. diff --git a/docs/standard/serialization/snippets/system-text-json-how-to-5-0/csharp/Fields.cs b/docs/standard/serialization/snippets/system-text-json-how-to-5-0/csharp/Fields.cs new file mode 100644 index 0000000000000..6101f6ec620e7 --- /dev/null +++ b/docs/standard/serialization/snippets/system-text-json-how-to-5-0/csharp/Fields.cs @@ -0,0 +1,67 @@ +using System; +using System.Text.Json; +using System.Text.Json.Serialization; + +namespace Fields +{ + public class Forecast + { + public DateTime Date; + public int TemperatureC; + public string Summary; + } + public class Forecast2 + { + [JsonInclude] + public DateTime Date; + [JsonInclude] + public int TemperatureC; + [JsonInclude] + public string Summary; + } + public class Program + { + public static void Main() + { + var json = "{\"Date\":\"2020-09-06T11:31:01.923395-07:00\",\"TemperatureC\":-1,\"Summary\":\"Cold\"} "; + Console.WriteLine($"Input JSON: {json}"); + + var options = new JsonSerializerOptions() + { + IncludeFields = true, + }; + var forecast = JsonSerializer.Deserialize(json, options); + + Console.WriteLine($"forecast.Date: {forecast.Date}"); + Console.WriteLine($"forecast.TemperatureC: {forecast.TemperatureC}"); + Console.WriteLine($"forecast.Summary: {forecast.Summary}"); + + var roundTrippedJson = JsonSerializer.Serialize + (forecast, options); + Console.WriteLine($"Output JSON: {roundTrippedJson}"); + + options = new JsonSerializerOptions(JsonSerializerDefaults.Web); + var forecast2 = JsonSerializer.Deserialize(json); + + Console.WriteLine($"forecast2.Date: {forecast2.Date}"); + Console.WriteLine($"forecast2.TemperatureC: {forecast2.TemperatureC}"); + Console.WriteLine($"forecast2.Summary: {forecast2.Summary}"); + + roundTrippedJson = JsonSerializer.Serialize + (forecast2, options); + Console.WriteLine($"Output JSON: {roundTrippedJson}"); + } + } +} + +// Produces output like the following example: +// +//Input JSON: { "date":"2020-09-06T11:31:01.923395-07:00","temperatureC":-1,"summary":"Cold"} +//forecast.Date: 9/6/2020 11:31:01 AM +//forecast.TemperatureC: -1 +//forecast.Summary: Cold +//Output JSON: { "date":"2020-09-06T11:31:01.923395-07:00","temperatureC":-1,"summary":"Cold"} +//forecast2.Date: 9/6/2020 11:31:01 AM +//forecast2.TemperatureC: -1 +//forecast2.Summary: Cold +//Output JSON: { "date":"2020-09-06T11:31:01.923395-07:00","temperatureC":-1,"summary":"Cold"} diff --git a/docs/standard/serialization/snippets/system-text-json-how-to-5-0/csharp/GuidReferenceResolverExample.cs b/docs/standard/serialization/snippets/system-text-json-how-to-5-0/csharp/GuidReferenceResolverExample.cs new file mode 100644 index 0000000000000..f0cfdd77df155 --- /dev/null +++ b/docs/standard/serialization/snippets/system-text-json-how-to-5-0/csharp/GuidReferenceResolverExample.cs @@ -0,0 +1,100 @@ +using System; +using System.Collections.Generic; +using System.Collections.Immutable; +using System.Text.Json; +using System.Text.Json.Serialization; + +namespace GuidReferenceResolverExample +{ + public class Person + { + internal Guid Id { get; set; } + public string Name { get; set; } + public Person Spouse { get; set; } + } + + public class GuidReferenceResolver : ReferenceResolver + { + private readonly IDictionary _people = new Dictionary(); + + public override object ResolveReference(string referenceId) + { + Guid id = new Guid(referenceId); + + _people.TryGetValue(id, out Person p); + + return p; + } + + public override string GetReference(object value, out bool alreadyExists) + { + Person p = (Person)value; + + if (!(alreadyExists = _people.ContainsKey(p.Id))) + { + _people[p.Id] = p; + } + + return p.Id.ToString(); + } + + public override void AddReference(string reference, object value) + { + Guid id = new Guid(reference); + Person person = (Person)value; + person.Id = id; + _people[id] = person; + } + } + + static class Program + { + public static void Main() + { + Person tyler = new() { Id = Guid.NewGuid(), Name = "Tyler" }; + Person adrian = new() { Id = Guid.NewGuid(), Name = "Adrian" }; + tyler.Spouse = adrian; + adrian.Spouse = tyler; + var people = ImmutableArray.Create(tyler, adrian); + + var options = new JsonSerializerOptions + { + WriteIndented = true, + ReferenceHandler = new ReferenceHandler() + }; + + string json = JsonSerializer.Serialize(people, options); + Console.WriteLine($"Output JSON {json}"); + + List peopleDeserialized = + JsonSerializer.Deserialize>(json, options); + + Person tylerDeserialized = people[0]; + Person adrianDeserialized = people[1]; + + Console.WriteLine($"Adrian is Tyler's spouse: {tylerDeserialized.Equals(adrianDeserialized.Spouse)}"); + Console.WriteLine($"Tyler is Adrian's spouse: {adrianDeserialized.Equals(tylerDeserialized.Spouse)}"); + } + } +} + +// Produces output like the following example: +// +//Output JSON[ +// { +// "$id": "79301726-9d94-499a-8cdc-0c8bcc4c9b63", +// "Name": "Tyler", +// "Spouse": { +// "$id": "94833059-35f2-4fdd-96ee-94fd0484969a", +// "Name": "Adrian", +// "Spouse": { +// "$ref": "79301726-9d94-499a-8cdc-0c8bcc4c9b63" +// } +// } +// }, +// { +// "$ref": "94833059-35f2-4fdd-96ee-94fd0484969a" +// } +//] +//Adrian is Tyler's spouse: True +//Tyler is Adrian's spouse: True diff --git a/docs/standard/serialization/snippets/system-text-json-how-to-5-0/csharp/HttpClientExtensionMethods.cs b/docs/standard/serialization/snippets/system-text-json-how-to-5-0/csharp/HttpClientExtensionMethods.cs new file mode 100644 index 0000000000000..38d94a685510e --- /dev/null +++ b/docs/standard/serialization/snippets/system-text-json-how-to-5-0/csharp/HttpClientExtensionMethods.cs @@ -0,0 +1,44 @@ +using System; +using System.Net.Http; +using System.Net.Http.Json; +using System.Threading.Tasks; + +namespace HttpClientExtensionMethods +{ + public class User + { + public int Id { get; set; } + public string Name { get; set; } + public string Username { get; set; } + public string Email { get; set; } + } + public class Program + { + public static async Task Main() + { + var client = new HttpClient(); + client.BaseAddress = new Uri("https://jsonplaceholder.typicode.com"); + + // Get the user information. + User user = await client.GetFromJsonAsync("users/1"); + Console.WriteLine($"Id: {user.Id}"); + Console.WriteLine($"Name: {user.Name}"); + Console.WriteLine($"Username: {user.Username}"); + Console.WriteLine($"Email: {user.Email}"); + + // Post a new user. + HttpResponseMessage response = await client.PostAsJsonAsync("users", user); + Console.WriteLine( + (response.IsSuccessStatusCode ? "Success" : "Error") + + $" - {response.StatusCode}"); + } + } +} + +// Produces output like the following example but with different names: +// +//Id: 1 +//Name: Tyler King +//Username: Tyler +//Email: Tyler @contoso.com +//Success - Created diff --git a/docs/standard/serialization/snippets/system-text-json-how-to-5-0/csharp/IgnoreNullOnSerialize.cs b/docs/standard/serialization/snippets/system-text-json-how-to-5-0/csharp/IgnoreNullOnSerialize.cs new file mode 100644 index 0000000000000..ff10b6547a4b4 --- /dev/null +++ b/docs/standard/serialization/snippets/system-text-json-how-to-5-0/csharp/IgnoreNullOnSerialize.cs @@ -0,0 +1,40 @@ +#nullable enable +using System; +using System.Text.Json; +using System.Text.Json.Serialization; + +namespace IgnoreNullOnSerialize +{ + public class Forecast + { + public DateTime Date { get; set; } + public int TemperatureC { get; set; } + public string? Summary { get; set; } + }; + + public class Program + { + public static void Main() + { + Forecast forecast = new() + { + Date = DateTime.Now, + Summary = null, + TemperatureC = default(int) + }; + + JsonSerializerOptions options = new() + { + DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull + }; + + string forecastJson = JsonSerializer.Serialize + (forecast, options); + Console.WriteLine(forecastJson); + } + } +} + +// Produces output like the following example: +// +//{"Date":"2020-10-30T10:11:40.2359135-07:00","TemperatureC":0} diff --git a/docs/standard/serialization/snippets/system-text-json-how-to-5-0/csharp/IgnoreValueDefaultOnSerialize.cs b/docs/standard/serialization/snippets/system-text-json-how-to-5-0/csharp/IgnoreValueDefaultOnSerialize.cs new file mode 100644 index 0000000000000..c62dd5ca843e1 --- /dev/null +++ b/docs/standard/serialization/snippets/system-text-json-how-to-5-0/csharp/IgnoreValueDefaultOnSerialize.cs @@ -0,0 +1,40 @@ +#nullable enable +using System; +using System.Text.Json; +using System.Text.Json.Serialization; + +namespace IgnoreValueDefaultOnSerialize +{ + public class Forecast + { + public DateTime Date { get; set; } + public int TemperatureC { get; set; } + public string? Summary { get; set; } + }; + + public class Program + { + public static void Main() + { + Forecast forecast = new() + { + Date = DateTime.Now, + Summary = null, + TemperatureC = default(int) + }; + + JsonSerializerOptions options = new() + { + DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingDefault + }; + + string forecastJson = JsonSerializer.Serialize + (forecast, options); + Console.WriteLine(forecastJson); + } + } +} + +// Produces output like the following example: +// +//{ "Date":"2020-10-21T15:40:06.8920138-07:00"} diff --git a/docs/standard/serialization/snippets/system-text-json-how-to-5-0/csharp/ImmutableTypes.cs b/docs/standard/serialization/snippets/system-text-json-how-to-5-0/csharp/ImmutableTypes.cs new file mode 100644 index 0000000000000..fcd6f90b32eb5 --- /dev/null +++ b/docs/standard/serialization/snippets/system-text-json-how-to-5-0/csharp/ImmutableTypes.cs @@ -0,0 +1,47 @@ +using System; +using System.Text.Json; +using System.Text.Json.Serialization; + +namespace ImmutableTypes +{ + public struct Forecast + { + public DateTime Date { get; } + public int TemperatureC { get; } + public string Summary { get; } + + [JsonConstructor] + public Forecast(DateTime date, int temperatureC, string summary) => + (Date, TemperatureC, Summary) = (date, temperatureC, summary); + } + public class Program + { + public static void Main() + { + var json = "{\"date\":\"2020-09-06T11:31:01.923395-07:00\",\"temperatureC\":-1,\"summary\":\"Cold\"} "; + Console.WriteLine($"Input JSON: {json}"); + + var options = new JsonSerializerOptions(JsonSerializerDefaults.Web); + + var forecast = JsonSerializer.Deserialize(json, options); + + Console.WriteLine($"forecast.Date: {forecast.Date}"); + Console.WriteLine($"forecast.TemperatureC: {forecast.TemperatureC}"); + Console.WriteLine($"forecast.Summary: {forecast.Summary}"); + + var roundTrippedJson = JsonSerializer.Serialize + (forecast, options); + + Console.WriteLine($"Output JSON: {roundTrippedJson}"); + + } + } +} + +// Produces output like the following example: +// +//Input JSON: { "date":"2020-09-06T11:31:01.923395-07:00","temperatureC":-1,"summary":"Cold"} +//forecast.Date: 9 / 6 / 2020 11:31:01 AM +//forecast.TemperatureC: -1 +//forecast.Summary: Cold +//Output JSON: { "date":"2020-09-06T11:31:01.923395-07:00","temperatureC":-1,"summary":"Cold"} diff --git a/docs/standard/serialization/snippets/system-text-json-how-to-5-0/csharp/JsonIgnoreAttributeExample.cs b/docs/standard/serialization/snippets/system-text-json-how-to-5-0/csharp/JsonIgnoreAttributeExample.cs new file mode 100644 index 0000000000000..7b7ac33e96e1e --- /dev/null +++ b/docs/standard/serialization/snippets/system-text-json-how-to-5-0/csharp/JsonIgnoreAttributeExample.cs @@ -0,0 +1,45 @@ +#nullable enable +using System; +using System.Text.Json; +using System.Text.Json.Serialization; + +namespace JsonIgnoreAttributeExample +{ + public class Forecast + { + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)] + public DateTime Date { get; set; } + + [JsonIgnore(Condition = JsonIgnoreCondition.Never)] + public int TemperatureC { get; set; } + + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + public string? Summary { get; set; } + }; + + public class Program + { + public static void Main() + { + Forecast forecast = new() + { + Date = default(DateTime), + Summary = null, + TemperatureC = default(int) + }; + + JsonSerializerOptions options = new() + { + DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingDefault + }; + + string forecastJson = JsonSerializer.Serialize + (forecast,options); + Console.WriteLine(forecastJson); + } + } +} + +// Produces output like the following example: +// +//{"TemperatureC":0} diff --git a/docs/standard/serialization/snippets/system-text-json-how-to-5-0/csharp/NonPublicAccessors.cs b/docs/standard/serialization/snippets/system-text-json-how-to-5-0/csharp/NonPublicAccessors.cs new file mode 100644 index 0000000000000..4c084f299c63f --- /dev/null +++ b/docs/standard/serialization/snippets/system-text-json-how-to-5-0/csharp/NonPublicAccessors.cs @@ -0,0 +1,41 @@ + +using System; +using System.Text.Json; +using System.Text.Json.Serialization; + +namespace NonPublicAccessors +{ + public class Forecast + { + public DateTime Date { get; init; } + + [JsonInclude] + public int TemperatureC { get; private set; } + + [JsonInclude] + public string Summary { private get; set; } + }; + + public class Program + { + public static void Main() + { + string json = "{\"Date\":\"2020-10-23T09:51:03.8702889-07:00\",\"TemperatureC\":40,\"Summary\":\"Hot\"}"; + Console.WriteLine($"Input JSON: {json}"); + + Forecast forecastDeserialized = JsonSerializer.Deserialize(json); + Console.WriteLine($"Date: {forecastDeserialized.Date}"); + Console.WriteLine($"TemperatureC: {forecastDeserialized.TemperatureC}"); + + json = JsonSerializer.Serialize(forecastDeserialized); + Console.WriteLine($"Output JSON: {json}"); + } + } +} + +// Produces output like the following example: +// +//Input JSON: { "Date":"2020-10-23T09:51:03.8702889-07:00","TemperatureC":40,"Summary":"Hot"} +//Date: 10 / 23 / 2020 9:51:03 AM +//TemperatureC: 40 +//Output JSON: { "Date":"2020-10-23T09:51:03.8702889-07:00","TemperatureC":40,"Summary":"Hot"} diff --git a/docs/standard/serialization/snippets/system-text-json-how-to-5-0/csharp/NonStringKeyDictionary.cs b/docs/standard/serialization/snippets/system-text-json-how-to-5-0/csharp/NonStringKeyDictionary.cs new file mode 100644 index 0000000000000..6d38f6e111bdf --- /dev/null +++ b/docs/standard/serialization/snippets/system-text-json-how-to-5-0/csharp/NonStringKeyDictionary.cs @@ -0,0 +1,43 @@ +using System; +using System.Collections.Generic; +using System.Text.Json; + +namespace NonStringKeyDictionary +{ + public class Program + { + public static void Main() + { + Dictionary numbers = new() + { + { 0, "zero" }, + { 1, "one" }, + { 34, "thirty four" }, + { 55, "fifty five" }, + }; + + JsonSerializerOptions options = new() + { + WriteIndented = true + }; + + string json = JsonSerializer.Serialize> + (numbers, options); + Console.WriteLine($"Output JSON: {json}"); + + var dictionary = JsonSerializer.Deserialize> + (json); + Console.WriteLine($"dictionary[55]: {dictionary[55]}"); + } + } +} + +// Produces output like the following example: +// +//Output JSON: { +// "0": "zero", +// "1": "one", +// "34": "thirty four", +// "55": "fifty five" +//} +//dictionary[55]: fifty five diff --git a/docs/standard/serialization/snippets/system-text-json-how-to-5-0/csharp/OptionsDefaults.cs b/docs/standard/serialization/snippets/system-text-json-how-to-5-0/csharp/OptionsDefaults.cs new file mode 100644 index 0000000000000..a1f4871c6fb49 --- /dev/null +++ b/docs/standard/serialization/snippets/system-text-json-how-to-5-0/csharp/OptionsDefaults.cs @@ -0,0 +1,61 @@ +using System; +using System.Text.Json; + +namespace OptionsDefaults +{ + public class Forecast + { + public DateTime Date { get; init; } + public int TemperatureC { get; set; } + public string Summary { get; set; } + }; + + public class Program + { + public static void Main() + { + Forecast forecast = new() + { + Date = DateTime.Now, + TemperatureC = 40, + Summary = "Hot" + }; + + JsonSerializerOptions options = new(JsonSerializerDefaults.Web) + { + WriteIndented = true + }; + + Console.WriteLine + ($"PropertyNameCaseInsensitive: {options.PropertyNameCaseInsensitive}"); + Console.WriteLine + ($"JsonNamingPolicy: {options.PropertyNamingPolicy}"); + Console.WriteLine + ($"NumberHandling: {options.NumberHandling}"); + + string forecastJson = JsonSerializer.Serialize(forecast, options); + Console.WriteLine($"Output JSON:\n{forecastJson}"); + + Forecast forecastDeserialized = + JsonSerializer.Deserialize(forecastJson, options); + Console.WriteLine($"Date: {forecastDeserialized.Date}"); + Console.WriteLine($"TemperatureC: {forecastDeserialized.TemperatureC}"); + Console.WriteLine($"Summary: {forecastDeserialized.Summary}"); + } + } +} + +// Produces output like the following example: +// +//PropertyNameCaseInsensitive: True +//JsonNamingPolicy: System.Text.Json.JsonCamelCaseNamingPolicy +//NumberHandling: AllowReadingFromString +//Output JSON: +//{ +// "date": "2020-10-21T15:40:06.9040831-07:00", +// "temperatureC": 40, +// "summary": "Hot" +//} +//Date: 10 / 21 / 2020 3:40:06 PM +//TemperatureC: 40 +//Summary: Hot diff --git a/docs/standard/serialization/snippets/system-text-json-how-to-5-0/csharp/PreserveReferences.cs b/docs/standard/serialization/snippets/system-text-json-how-to-5-0/csharp/PreserveReferences.cs new file mode 100644 index 0000000000000..f871e3cc3405d --- /dev/null +++ b/docs/standard/serialization/snippets/system-text-json-how-to-5-0/csharp/PreserveReferences.cs @@ -0,0 +1,72 @@ +using System; +using System.Collections.Generic; +using System.Text.Json; +using System.Text.Json.Serialization; + +namespace PreserveReferences +{ + public class Employee + { + public string Name { get; set; } + public Employee Manager { get; set; } + public List DirectReports { get; set; } + } + + public class Program + { + public static void Main() + { + Employee tyler = new() + { + Name = "Tyler Stein" + }; + + Employee adrian = new() + { + Name = "Adrian King" + }; + + tyler.DirectReports = new List { adrian }; + adrian.Manager = tyler; + + JsonSerializerOptions options = new() + { + ReferenceHandler = ReferenceHandler.Preserve, + WriteIndented = true + }; + + string tylerJson = JsonSerializer.Serialize(tyler, options); + Console.WriteLine($"Tyler serialized:\n{tylerJson}"); + + Employee tylerDeserialized = + JsonSerializer.Deserialize(tylerJson, options); + Console.WriteLine("Tyler is manager of Tyler's first direct report: "); + Console.WriteLine( + tylerDeserialized.DirectReports[0].Manager == tylerDeserialized); + } + } +} + +// Produces output like the following example: +// +//Tyler serialized: +//{ +// "$id": "1", +// "Name": "Tyler Stein", +// "Manager": null, +// "DirectReports": { +// "$id": "2", +// "$values": [ +// { +// "$id": "3", +// "Name": "Adrian King", +// "Manager": { +// "$ref": "1" +// }, +// "DirectReports": null +// } +// ] +// } +//} +//Tyler is manager of Tyler's first direct report: +//True diff --git a/docs/standard/serialization/snippets/system-text-json-how-to-5-0/csharp/Program.cs b/docs/standard/serialization/snippets/system-text-json-how-to-5-0/csharp/Program.cs new file mode 100644 index 0000000000000..9343d5c386c44 --- /dev/null +++ b/docs/standard/serialization/snippets/system-text-json-how-to-5-0/csharp/Program.cs @@ -0,0 +1,71 @@ +using System; +using System.Threading.Tasks; + +namespace SystemTextJsonHowTo +{ + class Program + { + static async Task Main(string[] args) + { + Console.WriteLine("======== Preserve references ========="); + PreserveReferences.Program.Main(); + Console.WriteLine(); + + Console.WriteLine("======== Immutable types ========="); + ImmutableTypes.Program.Main(); + Console.WriteLine(); + + Console.WriteLine("======== Record support ========="); + Records.Program.Main(); + Console.WriteLine(); + + Console.WriteLine("======== Field support ========="); + Fields.Program.Main(); + Console.WriteLine(); + + Console.WriteLine("======== Non-string key dictionary ========="); + NonStringKeyDictionary.Program.Main(); + Console.WriteLine(); + + Console.WriteLine("======== HttpClient extension methods ========="); + await HttpClientExtensionMethods.Program.Main(); + Console.WriteLine(); + + Console.WriteLine("======== Custom converter handle null ========="); + CustomConverterHandleNull.Program.Main(); + Console.WriteLine(); + + Console.WriteLine("======== Ignore value type default on serialize ========="); + IgnoreValueDefaultOnSerialize.Program.Main(); + Console.WriteLine(); + + Console.WriteLine("======== Ignore null on serialize ========="); + IgnoreNullOnSerialize.Program.Main(); + Console.WriteLine(); + + Console.WriteLine("======== Conditionally ignore selected properties on serialize ========="); + JsonIgnoreAttributeExample.Program.Main(); + Console.WriteLine(); + + Console.WriteLine("======== Non-public accessors ========="); + NonPublicAccessors.Program.Main(); + Console.WriteLine(); + + Console.WriteLine("======== Copy options instance ========="); + CopyOptions.Program.Main(); + Console.WriteLine(); + + Console.WriteLine("======== Create options instance with specified defaults ========="); + OptionsDefaults.Program.Main(); + Console.WriteLine(); + + Console.WriteLine("======== Quoted numbers ========="); + QuotedNumbers.Program.Main(); + Console.WriteLine(); + + Console.WriteLine("======== GuidReferenceResolver ========="); + GuidReferenceResolverExample.Program.Main(); + Console.WriteLine(); + } + } +} diff --git a/docs/standard/serialization/snippets/system-text-json-how-to-5-0/csharp/QuotedNumbers.cs b/docs/standard/serialization/snippets/system-text-json-how-to-5-0/csharp/QuotedNumbers.cs new file mode 100644 index 0000000000000..57af0bc724572 --- /dev/null +++ b/docs/standard/serialization/snippets/system-text-json-how-to-5-0/csharp/QuotedNumbers.cs @@ -0,0 +1,55 @@ +using System; +using System.Text.Json; +using System.Text.Json.Serialization; + +namespace QuotedNumbers +{ + public class Forecast + { + public DateTime Date { get; init; } + public int TemperatureC { get; set; } + public string Summary { get; set; } + }; + + public class Program + { + public static void Main() + { + Forecast forecast = new() + { + Date = DateTime.Now, + TemperatureC = 40, + Summary = "Hot" + }; + + JsonSerializerOptions options = new() + { + NumberHandling = JsonNumberHandling.AllowReadingFromString | + JsonNumberHandling.WriteAsString, + WriteIndented = true + }; + + string forecastJson = JsonSerializer.Serialize + (forecast, options); + Console.WriteLine($"Output JSON:\n{forecastJson}"); + + Forecast forecastDeserialized = + JsonSerializer.Deserialize(forecastJson, options); + Console.WriteLine($"Date: {forecastDeserialized.Date}"); + Console.WriteLine($"TemperatureC: {forecastDeserialized.TemperatureC}"); + Console.WriteLine($"Summary: {forecastDeserialized.Summary}"); + } + } +} + +// Produces output like the following example: +// +//Output JSON: +//{ +// "Date": "2020-10-23T12:27:06.4017385-07:00", +// "TemperatureC": "40", +// "Summary": "Hot" +//} +//Date: 10/23/2020 12:27:06 PM +//TemperatureC: 40 +//Summary: Hot diff --git a/docs/standard/serialization/snippets/system-text-json-how-to-5-0/csharp/Records.cs b/docs/standard/serialization/snippets/system-text-json-how-to-5-0/csharp/Records.cs new file mode 100644 index 0000000000000..3547b9bf6e009 --- /dev/null +++ b/docs/standard/serialization/snippets/system-text-json-how-to-5-0/csharp/Records.cs @@ -0,0 +1,32 @@ +#nullable enable +using System; +using System.Text.Json; + +namespace Records +{ + public record Forecast(DateTime Date, int TemperatureC) + { + public string? Summary { get; init; } + }; + + public class Program + { + public static void Main() + { + Forecast forecast = new(DateTime.Now, 40) + { + Summary = "Hot!" + }; + + string forecastJson = JsonSerializer.Serialize(forecast); + Console.WriteLine(forecastJson); + Forecast? forecastObj = JsonSerializer.Deserialize(forecastJson); + Console.WriteLine(forecastObj); + } + } +} + +// Produces output like the following example: +// +//{ "Date":"2020-10-21T15:26:10.5044594-07:00","TemperatureC":40,"Summary":"Hot!"} +//Forecast { Date = 10 / 21 / 2020 3:26:10 PM, TemperatureC = 40, Summary = Hot! } diff --git a/docs/standard/serialization/snippets/system-text-json-how-to-5-0/csharp/SystemTextJsonHowTo.csproj b/docs/standard/serialization/snippets/system-text-json-how-to-5-0/csharp/SystemTextJsonHowTo.csproj new file mode 100644 index 0000000000000..2588c49015844 --- /dev/null +++ b/docs/standard/serialization/snippets/system-text-json-how-to-5-0/csharp/SystemTextJsonHowTo.csproj @@ -0,0 +1,10 @@ + + + + Exe + net5.0 + 9.0 + SystemTextJsonHowTo.Program + + + diff --git a/docs/standard/serialization/system-text-json-converters-how-to.md b/docs/standard/serialization/system-text-json-converters-how-to.md index 1ef726238642d..6d16d1704766b 100644 --- a/docs/standard/serialization/system-text-json-converters-how-to.md +++ b/docs/standard/serialization/system-text-json-converters-how-to.md @@ -1,7 +1,9 @@ --- title: "How to write custom converters for JSON serialization - .NET" +description: "Learn how to create custom converters for the JSON serialization classes that are provided in the System.Text.Json namespace." ms.date: "01/10/2020" no-loc: [System.Text.Json, Newtonsoft.Json] +zone_pivot_groups: dotnet-version helpviewer_keywords: - "JSON serialization" - "serializing objects" @@ -21,10 +23,20 @@ A *converter* is a class that converts an object or a value to and from JSON. Th You can also write custom converters to customize or extend `System.Text.Json` with functionality not included in the current release. The following scenarios are covered later in this article: +::: zone pivot="dotnet-5-0" + +* [Deserialize inferred types to object properties](#deserialize-inferred-types-to-object-properties). +* [Support polymorphic deserialization](#support-polymorphic-deserialization). +* [Support round-trip for Stack\](#support-round-trip-for-stackt). +::: zone-end + +::: zone pivot="dotnet-core-3-1" + * [Deserialize inferred types to object properties](#deserialize-inferred-types-to-object-properties). * [Support Dictionary with non-string key](#support-dictionary-with-non-string-key). * [Support polymorphic deserialization](#support-polymorphic-deserialization). * [Support round-trip for Stack\](#support-round-trip-for-stackt). +::: zone-end ## Custom converter patterns @@ -170,10 +182,20 @@ A built-in converter is chosen only if no applicable custom converter is registe The following sections provide converter samples that address some common scenarios that built-in functionality doesn't handle. -* [Deserialize inferred types to object properties](#deserialize-inferred-types-to-object-properties) -* [Support Dictionary with non-string key](#support-dictionary-with-non-string-key) -* [Support polymorphic deserialization](#support-polymorphic-deserialization) +::: zone pivot="dotnet-5-0" + +* [Deserialize inferred types to object properties](#deserialize-inferred-types-to-object-properties). +* [Support polymorphic deserialization](#support-polymorphic-deserialization). * [Support round-trip for Stack\](#support-round-trip-for-stackt). +::: zone-end + +::: zone pivot="dotnet-core-3-1" + +* [Deserialize inferred types to object properties](#deserialize-inferred-types-to-object-properties). +* [Support Dictionary with non-string key](#support-dictionary-with-non-string-key). +* [Support polymorphic deserialization](#support-polymorphic-deserialization). +* [Support round-trip for Stack\](#support-round-trip-for-stackt). +::: zone-end ### Deserialize inferred types to object properties @@ -214,6 +236,8 @@ Without the custom converter, deserialization puts a `JsonElement` in each prope The [unit tests folder](https://github.com/dotnet/runtime/blob/81bf79fd9aa75305e55abe2f7e9ef3f60624a3a1/src/libraries/System.Text.Json/tests/Serialization/) in the `System.Text.Json.Serialization` namespace has more examples of custom converters that handle deserialization to `object` properties. +::: zone pivot="dotnet-core-3-1" + ### Support Dictionary with non-string key The built-in support for dictionary collections is for `Dictionary`. That is, the key must be a string. To support a dictionary with an integer or some other type as the key, a custom converter is required. @@ -245,6 +269,7 @@ The JSON output from serialization looks like the following example: ``` The [unit tests folder](https://github.com/dotnet/runtime/blob/81bf79fd9aa75305e55abe2f7e9ef3f60624a3a1/src/libraries/System.Text.Json/tests/Serialization/) in the `System.Text.Json.Serialization` namespace has more examples of custom converters that handle non-string-key dictionaries. +::: zone-end ### Support polymorphic deserialization @@ -281,7 +306,7 @@ The converter can deserialize JSON that was created by using the same converter The converter code in the preceding example reads and writes each property manually. An alternative is to call `Deserialize` or `Serialize` to do some of the work. For an example, see [this StackOverflow post](https://stackoverflow.com/a/59744873/12509023). -### Support round-trip for Stack\ +### Support round trip for Stack\ If you deserialize a JSON string into a object and then serialize that object, the contents of the stack are in reverse order. This behavior applies to the following types and interface, and user-defined types that derive from them: @@ -301,6 +326,29 @@ The following code registers the converter: [!code-csharp[](snippets/system-text-json-how-to/csharp/RoundtripStackOfT.cs?name=SnippetRegister)] +## Handle null values + +By default, the serializer handles null values as follows: + +* For reference types and `Nullable` types: + + * It does not pass `null` to custom converters on serialization. + * It does not pass `JsonTokenType.Null` to custom converters on deserialization. + * It returns a `null` instance on deserialization. + * It writes `null` directly with the writer on serialization. + +* For non-nullable value types: + + * It passes `JsonTokenType.Null` to custom converters on deserialization. (If no custom converter is available, a `JsonException` exception is thrown by the internal converter for the type.) + +This null-handling behavior is primarily to optimize performance by skipping an extra call to the converter. In addition, it avoids forcing converters for nullable types to check for `null` at the start of every `Read` and `Write` method override. + +::: zone pivot="dotnet-5-0" +To enable a custom converter to handle `null` for a reference or value type, override to return `true`, as shown in the following example: + +:::code language="csharp" source="snippets/system-text-json-how-to-5-0/csharp/CustomConverterHandleNull.cs" highlight="19"::: +::: zone-end + ## Other custom converter samples The [Migrate from Newtonsoft.Json to System.Text.Json](system-text-json-migrate-from-newtonsoft-how-to.md) article contains additional samples of custom converters. diff --git a/docs/standard/serialization/system-text-json-how-to.md b/docs/standard/serialization/system-text-json-how-to.md index 9081b753f3c02..4b332f6225223 100644 --- a/docs/standard/serialization/system-text-json-how-to.md +++ b/docs/standard/serialization/system-text-json-how-to.md @@ -1,8 +1,9 @@ --- title: "How to serialize and deserialize JSON using C# - .NET" -description: Learn how to use the System.Text.Json namespace to serialize to and deserialize from JSON in .NET. It includes sample code. -ms.date: 10/09/2020 +description: Learn how to use the System.Text.Json namespace to serialize to and deserialize from JSON in .NET. Includes sample code. +ms.date: 11/05/2020 no-loc: [System.Text.Json, Newtonsoft.Json] +zone_pivot_groups: dotnet-version helpviewer_keywords: - "JSON serialization" - "serializing objects" @@ -31,7 +32,7 @@ using System.Text.Json; using System.Text.Json.Serialization; ``` -Attributes from the namespace aren't currently supported in `System.Text.Json`. +Attributes from the namespace aren't supported in `System.Text.Json`. ## How to write .NET objects to JSON (serialize) @@ -108,15 +109,44 @@ A overload that takes a + * + * + * + * + * +::: zone-end + +::: zone pivot="dotnet-core-3-1" * .NET primitives that map to JavaScript primitives, such as numeric types, strings, and Boolean. * User-defined [plain old CLR objects (POCOs)](https://en.wikipedia.org/wiki/Plain_old_CLR_object). @@ -126,6 +156,10 @@ Supported types include: * * * + * + * + * +::: zone-end You can [implement custom converters](system-text-json-converters-how-to.md) to handle additional types or to provide functionality that isn't supported by the built-in converters. @@ -157,17 +191,34 @@ To deserialize from UTF-8, call a global setting or the [[JsonInclude]](xref:System.Text.Json.Serialization.JsonIncludeAttribute) attribute to include fields when serializing or deserializing, as shown in the following example: + +:::code language="csharp" source="snippets/system-text-json-how-to-5-0/csharp/Fields.cs" highlight="15,17,19,31"::: + +To ignore read-only fields, use the global setting. +::: zone-end + +::: zone pivot="dotnet-core-3-1" +Fields are not supported in System.Text.Json in .NET Core 3.1. [Custom converters](system-text-json-converters-how-to.md) can provide this functionality. +::: zone-end + ## Customize JSON names and values By default, property names and dictionary keys are unchanged in the JSON output, including case. Enum values are represented as numbers. This section explains how to: @@ -332,15 +397,26 @@ Enum string names can be deserialized as well, as shown in the following example [!code-csharp[](snippets/system-text-json-how-to/csharp/RoundtripEnumAsString.cs?name=SnippetDeserialize)] -## Exclude properties from serialization +## Ignore properties + +By default, all public properties are serialized. If you don't want some of them to appear in the JSON output, you have several options. This section explains how to ignore: + +::: zone pivot="dotnet-5-0" -By default, all public properties are serialized. If you don't want some of them to appear in the JSON output, you have several options. This section explains how to exclude: +* [Individual properties](#ignore-individual-properties) +* [All read-only properties](#ignore-all-read-only-properties) +* [All null-value properties](#ignore-all-null-value-properties) +* [All default-value properties](#ignore-all-default-value-properties) +::: zone-end -* [Individual properties](#exclude-individual-properties) -* [All read-only properties](#exclude-all-read-only-properties) -* [All null-value properties](#exclude-all-null-value-properties) +::: zone pivot="dotnet-core-3-1" -### Exclude individual properties +* [Individual properties](#ignore-individual-properties) +* [All read-only properties](#ignore-all-read-only-properties) +* [All null-value properties](#ignore-all-null-value-properties) +::: zone-end + +### Ignore individual properties To ignore individual properties, use the [[JsonIgnore]](xref:System.Text.Json.Serialization.JsonIgnoreAttribute) attribute. @@ -355,9 +431,22 @@ Here's an example type to serialize and JSON output: } ``` -### Exclude all read-only properties +::: zone pivot="dotnet-5-0" +You can specify conditional exclusion by setting the [[JsonIgnore]](xref:System.Text.Json.Serialization.JsonIgnoreAttribute) attribute's `Condition` property. The enum provides the following options: + +* `Always` - The property is always ignored. If no `Condition` is specified, this option is assumed. +* `Never` - The property is always serialized and deserialized, regardless of the `DefaultIgnoreCondition`, `IgnoreReadOnlyProperties`, and `IgnoreReadOnlyFields` global settings. +* `WhenWritingDefault` - The property is ignored on serialization if it's a reference type `null` or a value type `default`. +* `WhenWritingNull` - The property is ignored on serialization if it's a reference type `null`. + +The following example illustrates use of the [[JsonIgnore]](xref:System.Text.Json.Serialization.JsonIgnoreAttribute) attribute's `Condition` property: -A property is read-only if it contains a public getter but not a public setter. To exclude all read-only properties, set the to `true`, as shown in the following example: +:::code language="csharp" source="snippets/system-text-json-how-to-5-0/csharp/JsonIgnoreAttributeExample.cs" highlight="10,13,16"::: +::: zone-end + +### Ignore all read-only properties + +A property is read-only if it contains a public getter but not a public setter. To ignore all read-only properties when serializing, set the to `true`, as shown in the following example: [!code-csharp[](snippets/system-text-json-how-to/csharp/SerializeExcludeReadOnlyProperties.cs?name=SnippetSerialize)] @@ -375,9 +464,21 @@ Here's an example type to serialize and JSON output: This option applies only to serialization. During deserialization, read-only properties are ignored by default. -### Exclude all null value properties +::: zone pivot="dotnet-5-0" +This option applies only to properties. To ignore read-only fields when [serializing fields](#include-fields), use the global setting. +::: zone-end + +### Ignore all null-value properties -To exclude all null value properties, set the property to `true`, as shown in the following example: +::: zone pivot="dotnet-5-0" +To ignore all null-value properties, set the property to , as shown in the following example: + +:::code language="csharp" source="snippets/system-text-json-how-to-5-0/csharp/IgnoreNullOnSerialize.cs" highlight="28"::: + +::: zone-end + +::: zone pivot="dotnet-core-3-1" +To ignore all null-value properties when serializing, set the property to `true`, as shown in the following example: [!code-csharp[](snippets/system-text-json-how-to/csharp/SerializeExcludeNullValueProperties.cs?name=SnippetSerialize)] @@ -396,7 +497,21 @@ Here's an example object to serialize and JSON output: } ``` -This setting applies to serialization and deserialization. For information about its effect on deserialization, see [Ignore null when deserializing](#ignore-null-when-deserializing). +::: zone-end + +### Ignore all default-value properties + +::: zone pivot="dotnet-5-0" +To prevent serialization of default values in value type properties, set the property to , as shown in the following example: + +:::code language="csharp" source="snippets/system-text-json-how-to-5-0/csharp/IgnoreValueDefaultOnSerialize.cs" highlight="28"::: +::: zone-end + +The `WhenWritingDefault` setting also prevents serialization of null-value reference type properties. + +::: zone pivot="dotnet-core-3-1" +There is no built-in way to prevent serialization of properties with value type defaults in System.Text.Json in .NET Core 3.1. +::: zone-end ## Customize character encoding @@ -633,7 +748,7 @@ And the JSON to be deserialized is this: } ``` -If you deserialize the JSON shown into the type shown, the `DatesAvailable` and `SummaryWords` properties have nowhere to go and are lost. To capture extra data such as these properties, apply the [JsonExtensionData](xref:System.Text.Json.Serialization.JsonExtensionDataAttribute) attribute to a property of type `Dictionary` or `Dictionary`: +If you deserialize the JSON shown into the type shown, the `DatesAvailable` and `SummaryWords` properties have nowhere to go and are lost. To capture extra data such as these properties, apply the [[JsonExtensionData]](xref:System.Text.Json.Serialization.JsonExtensionDataAttribute) attribute to a property of type `Dictionary` or `Dictionary`: [!code-csharp[](snippets/system-text-json-how-to/csharp/WeatherForecast.cs?name=SnippetWFWithExtensionData)] @@ -670,33 +785,138 @@ When the target object is serialized, the extension data key value pairs become Notice that the `ExtensionData` property name doesn't appear in the JSON. This behavior lets the JSON make a round trip without losing any extra data that otherwise wouldn't be deserialized. -## Ignore null when deserializing +## Preserve references and handle circular references -By default, if a property in JSON is null, the corresponding property in the target object is set to null. In some scenarios, the target property might have a default value, and you don't want a null value to override the default. +::: zone pivot="dotnet-5-0" -For example, suppose the following code represents your target object: +To preserve references and handle circular references, set to . This setting causes the following behavior: -[!code-csharp[](snippets/system-text-json-how-to/csharp/WeatherForecast.cs?name=SnippetWFWithDefault)] +* On serialize: -And suppose the following JSON is deserialized: + When writing complex types, the serializer also writes metadata properties (`$id`, `$values`, and `$ref`). -```json -{ - "Date": "2019-08-01T00:00:00-07:00", - "TemperatureCelsius": 25, - "Summary": null -} -``` +* On deserialize: + + Metadata is expected (although not mandatory), and the deserializer tries to understand it. + +The following code illustrates use of the `Preserve` setting. + +:::code language="csharp" source="snippets/system-text-json-how-to-5-0/csharp/PreserveReferences.cs" highlight="34"::: + +This feature can't be used to preserve value types or immutable types. On deserialization, the instance of an immutable type is created after the entire payload is read. So it would be impossible to deserialize the same instance if a reference to it appears within the JSON payload. + +For value types, immutable types, and arrays, no reference metadata is serialized. On deserialization, an exception is thrown if `$ref` or `$id` is found. However, value types ignore `$id` (and `$values` in the case of collections) to make it possible to deserialize payloads that were serialized by using Newtonsoft.Json. Newtonsoft.Json does serialize metadata for such types. + +To determine if objects are equal, System.Text.Json uses , which uses reference equality () instead of value equality ( when comparing two object instances. + +For more information about how references are serialized and deserialized, see . + +The class defines the behavior of preserving references on serialization and deserialization. Create a derived class to specify custom behavior. For an example, see [GuidReferenceResolver](https://github.com/dotnet/dotnet-docs/blob/master/docs/standard/serialization/snippets/system-text-json-how-to-5-0/csharp/GuidReferenceResolver.cs). + +::: zone-end + +::: zone pivot="dotnet-core-3-1" +System.Text.Json in .NET Core 3.1 only supports serialization by value and throws an exception for circular references. +::: zone-end + +## Allow or write numbers in quotes + +::: zone pivot="dotnet-5-0" + +Some serializers encode numbers as JSON strings (surrounded by quotes). For example: `{"DegreesCelsius":"23"}` instead of `{"DegreesCelsius":23}`. To serialize numbers in quotes or accept numbers in quotes across the entire input object graph, set as shown in the following example: + +:::code language="csharp" source="snippets/system-text-json-how-to-5-0/csharp/QuotedNumbers.cs" highlight="27-28"::: + +When you use System.Text.Json indirectly through ASP.NET Core, quoted numbers are allowed when deserializing because ASP.NET Core specifies [web default options](xref:System.Text.Json.JsonSerializerDefaults.Web). + +To allow or write quoted numbers for specific properties, fields, or types, use the [[JsonNumberHandling]](xref:System.Text.Json.Serialization.JsonNumberHandlingAttribute) attribute. +::: zone-end + +::: zone pivot="dotnet-core-3-1" +System.Text.Json in .NET Core 3.1 doesn't support serializing or deserializing numbers surrounded by quotation marks. For more information, see [Allow or write numbers in quotes](system-text-json-migrate-from-newtonsoft-how-to.md#allow-or-write-numbers-in-quotes). +::: zone-end + +## Immutable types and Records + +::: zone pivot="dotnet-5-0" +System.Text.Json can use a parameterized constructor, which makes it possible to deserialize an immutable class or struct. For a class, if the only constructor is a parameterized one, that constructor will be used. For a struct, or a class with multiple constructors, specify the one to use by applying the [[JsonConstructor]](xref:System.Text.Json.Serialization.JsonConstructorAttribute.%23ctor%2A) attribute. When the attribute is not used, a public parameterless constructor is always used if present. The attribute can only be used with public constructors. The following example uses the `[JsonConstructor]` attribute: + +:::code language="csharp" source="snippets/system-text-json-how-to-5-0/csharp/ImmutableTypes.cs" highlight="13"::: + +Records in C# 9 are also supported, as shown in the following example: + +:::code language="csharp" source="snippets/system-text-json-how-to-5-0/csharp/Records.cs"::: + +For types that are immutable because all their property setters are non-public, see the following section about [non-public property accessors](#non-public-property-accessors). +::: zone-end + +::: zone pivot="dotnet-core-3-1" +`JsonConstructorAttribute` and C# 9 Record support aren't available in .NET Core 3.1. +::: zone-end + +## Non-public property accessors + +::: zone pivot="dotnet-5-0" +To enable use of a non-public property accessor, use the [[JsonInclude]](xref:System.Text.Json.Serialization.JsonIncludeAttribute) attribute, as shown in the following example: + +:::code language="csharp" source="snippets/system-text-json-how-to-5-0/csharp/NonPublicAccessors.cs" highlight="12,15"::: +::: zone-end + +::: zone pivot="dotnet-core-3-1" +Non-public property accessors are not supported in .NET Core 3.1. For more information, see [the Migrate from Newtonsoft.Json article](system-text-json-migrate-from-newtonsoft-how-to.md#non-public-property-setters-and-getters). +::: zone-end + +## Copy JsonSerializerOptions + +::: zone pivot="dotnet-5-0" +There is a [JsonSerializerOptions constructor](xref:System.Text.Json.JsonSerializerOptions.%23ctor(System.Text.Json.JsonSerializerOptions)) that lets you create a new instance with the same options as an existing instance, as shown in the following example: + +:::code language="csharp" source="snippets/system-text-json-how-to-5-0/csharp/CopyOptions.cs" highlight="29"::: +::: zone-end + +::: zone pivot="dotnet-core-3-1" +A `JsonSerializerOptions` constructor that takes an existing instance is not available in .NET Core 3.1. +::: zone-end + +## Web defaults for JsonSerializerOptions + +::: zone pivot="dotnet-5-0" +Here are the options that have different defaults for web apps: + +* = `true` +* = +* = + +There's a [JsonSerializerOptions constructor](xref:System.Text.Json.JsonSerializerOptions.%23ctor(System.Text.Json.JsonSerializerDefaults)?view=net-5.0&preserve-view=true) that lets you create a new instance with the default options that ASP.NET Core uses for web apps, as shown in the following example: + +:::code language="csharp" source="snippets/system-text-json-how-to-5-0/csharp/OptionsDefaults.cs" highlight="24"::: +::: zone-end + +::: zone pivot="dotnet-core-3-1" +Here are the options that have different defaults for web apps: + +* = `true` +* = + +A `JsonSerializerOptions` constructor that specifies a set of defaults is not available in .NET Core 3.1. +::: zone-end + +## HttpClient and HttpContent extension methods + +::: zone pivot="dotnet-5-0" -After deserialization, the `Summary` property of the `WeatherForecastWithDefault` object is null. +Serializing and deserializing JSON payloads from the network are common operations. Extension methods on [HttpClient](xref:System.Net.Http.Json.HttpClientJsonExtensions) and [HttpContent](xref:System.Net.Http.Json.HttpContentJsonExtensions) let you do these operations in a single line of code. These extension methods use [web defaults for JsonSerializerOptions](#web-defaults-for-jsonserializeroptions). -To change this behavior, set to `true`, as shown in the following example: +The following example illustrates use of and : -[!code-csharp[](snippets/system-text-json-how-to/csharp/DeserializeIgnoreNull.cs?name=SnippetDeserialize)] +:::code language="csharp" source="snippets/system-text-json-how-to-5-0/csharp/HttpClientExtensionMethods.cs" highlight="23,30"::: -With this option, the `Summary` property of the `WeatherForecastWithDefault` object is the default value "No summary" after deserialization. +There are also extension methods for System.Text.Json on [HttpContent](xref:System.Net.Http.Json.HttpContentJsonExtensions). +::: zone-end -Null values in the JSON are ignored only if they are valid. Null values for non-nullable value types cause exceptions. +::: zone pivot="dotnet-core-3-1" +Extension methods on `HttpClient` and `HttpContent` are not available in System.Text.Json in .NET Core 3.1. +::: zone-end ## Utf8JsonReader, Utf8JsonWriter, and JsonDocument @@ -789,17 +1009,17 @@ When reading a large file (a gigabyte or more in size, for example), you might w When using the `Utf8JsonReader` to read from a stream, the following rules apply: -* The buffer containing the partial JSON payload must be at least as big as the largest JSON token within it so that the reader can make forward progress. -* The buffer must be at least as big as the largest sequence of white space within the JSON. +* The buffer containing the partial JSON payload must be at least as large as the largest JSON token within it so that the reader can make forward progress. +* The buffer must be at least as large as the largest sequence of white space within the JSON. * The reader doesn't keep track of the data it has read until it completely reads the next in the JSON payload. So when there are bytes left over in the buffer, you have to pass them to the reader again. You can use to determine how many bytes are left over. The following code illustrates how to read from a stream. The example shows a . Similar code will work with a , except when the `FileStream` contains a UTF-8 BOM at the start. In that case, you need to strip those three bytes from the buffer before passing the remaining bytes to the `Utf8JsonReader`. Otherwise the reader would throw an exception, since the BOM is not considered a valid part of the JSON. -The sample code starts with a 4KB buffer and doubles the buffer size each time it finds that the size is not big enough to fit a complete JSON token, which is required for the reader to make forward progress on the JSON payload. The JSON sample provided in the snippet triggers a buffer size increase only if you set a very small initial buffer size, for example, 10 bytes. If you set the initial buffer size to 10, the `Console.WriteLine` statements illustrate the cause and effect of buffer size increases. At the 4KB initial buffer size, the entire sample JSON is shown by each `Console.WriteLine`, and the buffer size never has to be increased. +The sample code starts with a 4KB buffer and doubles the buffer size each time it finds that the size is not large enough to fit a complete JSON token, which is required for the reader to make forward progress on the JSON payload. The JSON sample provided in the snippet triggers a buffer size increase only if you set a very small initial buffer size, for example, 10 bytes. If you set the initial buffer size to 10, the `Console.WriteLine` statements illustrate the cause and effect of buffer size increases. At the 4KB initial buffer size, the entire sample JSON is shown by each `Console.WriteLine`, and the buffer size never has to be increased. [!code-csharp[](snippets/system-text-json-how-to/csharp/Utf8ReaderPartialRead.cs)] -The preceding example sets no limit to how big the buffer can grow. If the token size is too large, the code could fail with an exception. This can happen if the JSON contains a token that is around 1 GB or more in size, because doubling the 1 GB size results in a size that is too large to fit into an `int32` buffer. +The preceding example sets no limit to how large the buffer can grow. If the token size is too large, the code could fail with an exception. This can happen if the JSON contains a token that is around 1 GB or more in size, because doubling the 1 GB size results in a size that is too large to fit into an `int32` buffer. ## Additional resources diff --git a/docs/standard/serialization/system-text-json-migrate-from-newtonsoft-how-to.md b/docs/standard/serialization/system-text-json-migrate-from-newtonsoft-how-to.md index 3526e507866cf..cda985c6a2fd9 100644 --- a/docs/standard/serialization/system-text-json-migrate-from-newtonsoft-how-to.md +++ b/docs/standard/serialization/system-text-json-migrate-from-newtonsoft-how-to.md @@ -1,9 +1,11 @@ --- title: "Migrate from Newtonsoft.Json to System.Text.Json - .NET" +description: "Learn how to migrate from Newtonsoft.Json to System.Text.Json. Includes sample code." author: tdykstra ms.author: tdykstra no-loc: [System.Text.Json, Newtonsoft.Json] -ms.date: "01/10/2020" +ms.date: 11/05/2020 +zone_pivot_groups: dotnet-version helpviewer_keywords: - "JSON serialization" - "serializing objects" @@ -15,7 +17,7 @@ helpviewer_keywords: This article shows how to migrate from [Newtonsoft.Json](https://www.newtonsoft.com/json) to . -The `System.Text.Json` namespace provides functionality for serializing to and deserializing from JavaScript Object Notation (JSON). The `System.Text.Json` library is included in the shared framework for .NET Core 3.0 and later versions. For earlier framework versions, install the [System.Text.Json](https://www.nuget.org/packages/System.Text.Json) NuGet package. The package supports: +The `System.Text.Json` namespace provides functionality for serializing to and deserializing from JavaScript Object Notation (JSON). The `System.Text.Json` library is included in the runtime for [.NET Core 3.1](https://dotnet.microsoft.com/download/dotnet-core/3.1) and later versions. For other target frameworks, install the [System.Text.Json](https://www.nuget.org/packages/System.Text.Json) NuGet package. The package supports: * .NET Standard 2.0 and later versions * .NET Framework 4.7.2 and later versions @@ -23,8 +25,6 @@ The `System.Text.Json` namespace provides functionality for serializing to and d `System.Text.Json` focuses primarily on performance, security, and standards compliance. It has some key differences in default behavior and doesn't aim to have feature parity with `Newtonsoft.Json`. For some scenarios, `System.Text.Json` has no built-in functionality, but there are recommended workarounds. For other scenarios, workarounds are impractical. If your application depends on a missing feature, consider [filing an issue](https://github.com/dotnet/runtime/issues/new) to find out if support for your scenario can be added. - - Most of this article is about how to use the API, but it also includes guidance on how to use the (which represents the Document Object Model or DOM), , and types. ## Table of differences between Newtonsoft.Json and System.Text.Json @@ -35,34 +35,76 @@ The following table lists `Newtonsoft.Json` features and `System.Text.Json` equi * Not supported, workaround is possible. The workarounds are [custom converters](system-text-json-converters-how-to.md), which may not provide complete parity with `Newtonsoft.Json` functionality. For some of these, sample code is provided as examples. If you rely on these `Newtonsoft.Json` features, migration will require modifications to your .NET object models or other code changes. * Not supported, workaround is not practical or possible. If you rely on these `Newtonsoft.Json` features, migration will not be possible without significant changes. +::: zone pivot="dotnet-5-0" +| Newtonsoft.Json feature | System.Text.Json equivalent | +|-------------------------------------------------------|-----------------------------| +| Case-insensitive deserialization by default | ✔️ [PropertyNameCaseInsensitive global setting](#case-insensitive-deserialization) | +| Camel-case property names | ✔️ [PropertyNamingPolicy global setting](system-text-json-how-to.md#use-camel-case-for-all-json-property-names) | +| Minimal character escaping | ✔️ [Strict character escaping, configurable](#minimal-character-escaping) | +| `NullValueHandling.Ignore` global setting | ✔️ [DefaultIgnoreCondition global option](system-text-json-how-to.md#ignore-all-null-value-properties) |[Conditionally ignore a property](#conditionally-ignore-a-property) +| Allow comments | ✔️ [ReadCommentHandling global setting](#comments) | +| Allow trailing commas | ✔️ [AllowTrailingCommas global setting](#trailing-commas) | +| Custom converter registration | ✔️ [Order of precedence differs](#converter-registration-precedence) | +| No maximum depth by default | ✔️ [Default maximum depth 64, configurable](#maximum-depth) | +| `PreserveReferencesHandling` global setting | ✔️ [ReferenceHandling global setting](#preserve-object-references-and-handle-loops) | +| `ReferenceLoopHandling` global setting | ✔️ [ReferenceHandling global setting](#preserve-object-references-and-handle-loops) | +| Serialize or deserialize numbers in quotes | ✔️ [NumberHandling global setting, [JsonNumberHandling] attribute](#allow-or-write-numbers-in-quotes) | +| Deserialize to immutable classes and structs | ✔️ [JsonConstructor, C# 9 Records](#deserialize-to-immutable-classes-and-structs) | +| Support for fields | ✔️ [IncludeFields global setting, [JsonInclude] attribute](#public-and-non-public-fields) | +| `DefaultValueHandling` global setting | ✔️ [DefaultIgnoreCondition global setting](#conditionally-ignore-a-property) | +| `NullValueHandling` setting on `[JsonProperty]` | ✔️ [JsonIgnore attribute](#conditionally-ignore-a-property) | +| `DefaultValueHandling` setting on `[JsonProperty]` | ✔️ [JsonIgnore attribute](#conditionally-ignore-a-property) | +| Deserialize `Dictionary` with non-string key | ✔️ [Supported](#dictionary-with-non-string-key) | +| Support for non-public property setters and getters | ✔️ [JsonInclude attribute](#non-public-property-setters-and-getters) | +| `[JsonConstructor]` attribute | ✔️ [[JsonConstructor] attribute](#specify-constructor-to-use-when-deserializing) | +| Support for a broad range of types | ⚠️ [Some types require custom converters](#types-without-built-in-support) | +| Polymorphic serialization | ⚠️ [Not supported, workaround, sample](#polymorphic-serialization) | +| Polymorphic deserialization | ⚠️ [Not supported, workaround, sample](#polymorphic-deserialization) | +| Deserialize inferred type to `object` properties | ⚠️ [Not supported, workaround, sample](#deserialization-of-object-properties) | +| Deserialize JSON `null` literal to non-nullable value types | ⚠️ [Not supported, workaround, sample](#deserialize-null-to-non-nullable-type) | +| `Required` setting on `[JsonProperty]` attribute | ⚠️ [Not supported, workaround, sample](#required-properties) | +| `DefaultContractResolver` to ignore properties | ⚠️ [Not supported, workaround, sample](#conditionally-ignore-a-property) | +| `DateTimeZoneHandling`, `DateFormatString` settings | ⚠️ [Not supported, workaround, sample](#specify-date-format) | +| Callbacks | ⚠️ [Not supported, workaround, sample](#callbacks) | +| `JsonConvert.PopulateObject` method | ⚠️ [Not supported, workaround](#populate-existing-objects) | +| `ObjectCreationHandling` global setting | ⚠️ [Not supported, workaround](#reuse-rather-than-replace-properties) | +| Add to collections without setters | ⚠️ [Not supported, workaround](#add-to-collections-without-setters) | +| Support for `System.Runtime.Serialization` attributes | ❌ [Not supported](#systemruntimeserialization-attributes) | +| `MissingMemberHandling` global setting | ❌ [Not supported](#missingmemberhandling) | +| Allow property names without quotes | ❌ [Not supported](#json-strings-property-names-and-string-values) | +| Allow single quotes around string values | ❌ [Not supported](#json-strings-property-names-and-string-values) | +| Allow non-string JSON values for string properties | ❌ [Not supported](#non-string-values-for-string-properties) | +::: zone-end + +::: zone pivot="dotnet-core-3-1" | Newtonsoft.Json feature | System.Text.Json equivalent | |-------------------------------------------------------|-----------------------------| | Case-insensitive deserialization by default | ✔️ [PropertyNameCaseInsensitive global setting](#case-insensitive-deserialization) | | Camel-case property names | ✔️ [PropertyNamingPolicy global setting](system-text-json-how-to.md#use-camel-case-for-all-json-property-names) | | Minimal character escaping | ✔️ [Strict character escaping, configurable](#minimal-character-escaping) | -| `NullValueHandling.Ignore` global setting | ✔️ [IgnoreNullValues global option](system-text-json-how-to.md#exclude-all-null-value-properties) | +| `NullValueHandling.Ignore` global setting | ✔️ [IgnoreNullValues global option](system-text-json-how-to.md#ignore-all-null-value-properties) | | Allow comments | ✔️ [ReadCommentHandling global setting](#comments) | | Allow trailing commas | ✔️ [AllowTrailingCommas global setting](#trailing-commas) | | Custom converter registration | ✔️ [Order of precedence differs](#converter-registration-precedence) | | No maximum depth by default | ✔️ [Default maximum depth 64, configurable](#maximum-depth) | | Support for a broad range of types | ⚠️ [Some types require custom converters](#types-without-built-in-support) | -| Deserialize strings as numbers | ⚠️ [Not supported, workaround, sample](#quoted-numbers) | +| Deserialize strings as numbers | ⚠️ [Not supported, workaround, sample](#allow-or-write-numbers-in-quotes) | | Deserialize `Dictionary` with non-string key | ⚠️ [Not supported, workaround, sample](#dictionary-with-non-string-key) | | Polymorphic serialization | ⚠️ [Not supported, workaround, sample](#polymorphic-serialization) | | Polymorphic deserialization | ⚠️ [Not supported, workaround, sample](#polymorphic-deserialization) | | Deserialize inferred type to `object` properties | ⚠️ [Not supported, workaround, sample](#deserialization-of-object-properties) | | Deserialize JSON `null` literal to non-nullable value types | ⚠️ [Not supported, workaround, sample](#deserialize-null-to-non-nullable-type) | | Deserialize to immutable classes and structs | ⚠️ [Not supported, workaround, sample](#deserialize-to-immutable-classes-and-structs) | -| `[JsonConstructor]` attribute | ⚠️ [Not supported, workaround, sample](#specify-constructor-to-use) | +| `[JsonConstructor]` attribute | ⚠️ [Not supported, workaround, sample](#specify-constructor-to-use-when-deserializing) | | `Required` setting on `[JsonProperty]` attribute | ⚠️ [Not supported, workaround, sample](#required-properties) | | `NullValueHandling` setting on `[JsonProperty]` attribute | ⚠️ [Not supported, workaround, sample](#conditionally-ignore-a-property) | | `DefaultValueHandling` setting on `[JsonProperty]` attribute | ⚠️ [Not supported, workaround, sample](#conditionally-ignore-a-property) | | `DefaultValueHandling` global setting | ⚠️ [Not supported, workaround, sample](#conditionally-ignore-a-property) | -| `DefaultContractResolver` to exclude properties | ⚠️ [Not supported, workaround, sample](#conditionally-ignore-a-property) | +| `DefaultContractResolver` to ignore properties | ⚠️ [Not supported, workaround, sample](#conditionally-ignore-a-property) | | `DateTimeZoneHandling`, `DateFormatString` settings | ⚠️ [Not supported, workaround, sample](#specify-date-format) | | Callbacks | ⚠️ [Not supported, workaround, sample](#callbacks) | | Support for public and non-public fields | ⚠️ [Not supported, workaround](#public-and-non-public-fields) | -| Support for internal and private property setters and getters | ⚠️ [Not supported, workaround](#internal-and-private-property-setters-and-getters) | +| Support for non-public property setters and getters | ⚠️ [Not supported, workaround](#non-public-property-setters-and-getters) | | `JsonConvert.PopulateObject` method | ⚠️ [Not supported, workaround](#populate-existing-objects) | | `ObjectCreationHandling` global setting | ⚠️ [Not supported, workaround](#reuse-rather-than-replace-properties) | | Add to collections without setters | ⚠️ [Not supported, workaround](#add-to-collections-without-setters) | @@ -73,6 +115,7 @@ The following table lists `Newtonsoft.Json` features and `System.Text.Json` equi | Allow property names without quotes | ❌ [Not supported](#json-strings-property-names-and-string-values) | | Allow single quotes around string values | ❌ [Not supported](#json-strings-property-names-and-string-values) | | Allow non-string JSON values for string properties | ❌ [Not supported](#non-string-values-for-string-properties) | +::: zone-end This is not an exhaustive list of `Newtonsoft.Json` features. The list includes many of the scenarios that have been requested in [GitHub issues](https://github.com/dotnet/runtime/issues?q=is%3Aopen+is%3Aissue+label%3Aarea-System.Text.Json) or [StackOverflow](https://stackoverflow.com/questions/tagged/system.text.json) posts. If you implement a workaround for one of the scenarios listed here that doesn't currently have sample code, and if you want to share your solution, select **This page** in the **Feedback** section at the bottom of this page. That creates an issue in this documentation's GitHub repo and lists it in the **Feedback** section on this page too. @@ -84,7 +127,11 @@ This is not an exhaustive list of `Newtonsoft.Json` features. The list includes During deserialization, `Newtonsoft.Json` does case-insensitive property name matching by default. The default is case-sensitive, which gives better performance since it's doing an exact match. For information about how to do case-insensitive matching, see [Case-insensitive property matching](system-text-json-how-to.md#case-insensitive-property-matching). -If you're using `System.Text.Json` indirectly by using ASP.NET Core, you don't need to do anything to get behavior like `Newtonsoft.Json`. ASP.NET Core specifies the settings for [camel-casing property names](system-text-json-how-to.md#use-camel-case-for-all-json-property-names) and case-insensitive matching when it uses `System.Text.Json`. The default values are set in the [JsonOptions class](https://github.com/dotnet/aspnetcore/blob/1f56888ea03f6a113587a6c4ac4d8b2ded326ffa/src/Mvc/Mvc.Core/src/JsonOptions.cs#L22-L28). +If you're using `System.Text.Json` indirectly by using ASP.NET Core, you don't need to do anything to get behavior like `Newtonsoft.Json`. ASP.NET Core specifies the settings for [camel-casing property names](system-text-json-how-to.md#use-camel-case-for-all-json-property-names) and case-insensitive matching when it uses `System.Text.Json`. + +::: zone pivot="dotnet-5-0" +ASP.NET Core also enables deserializing [quoted numbers](#allow-or-write-numbers-in-quotes) by default. +::: zone-end ### Minimal character escaping @@ -171,29 +218,24 @@ public class ExampleClass The JSON value could not be converted to System.String. ``` -## Scenarios using JsonSerializer that require workarounds +## Scenarios using JsonSerializer -The following scenarios aren't supported by built-in functionality, but workarounds are possible. The workarounds are [custom converters](system-text-json-converters-how-to.md), which may not provide complete parity with `Newtonsoft.Json` functionality. For some of these, sample code is provided as examples. If you rely on these `Newtonsoft.Json` features, migration will require modifications to your .NET object models or other code changes. +Some of the following scenarios aren't supported by built-in functionality, but workarounds are possible. The workarounds are [custom converters](system-text-json-converters-how-to.md), which may not provide complete parity with `Newtonsoft.Json` functionality. For some of these, sample code is provided as examples. If you rely on these `Newtonsoft.Json` features, migration will require modifications to your .NET object models or other code changes. -### Types without built-in support +For some of the following scenarios, workarounds are not practical or possible. If you rely on these `Newtonsoft.Json` features, migration will not be possible without significant changes. - doesn't provide built-in support for the following types: +### Allow or write numbers in quotes -* and related types -* F# types, such as [discriminated unions](../../fsharp/language-reference/discriminated-unions.md), [record types](../../fsharp/language-reference/records.md), and [anonymous record types](../../fsharp/language-reference/anonymous-records.md). -* -* -* -* -* -* -* and its associated generic types +::: zone pivot="dotnet-5-0" +`Newtonsoft.Json` can serialize or deserialize numbers represented by JSON strings (surrounded by quotes). For example, it can accept: `{"DegreesCelsius":"23"}` instead of `{"DegreesCelsius":23}`. To enable that behavior in , set to or , or use the [[JsonNumberHandling]](xref:System.Text.Json.Serialization.JsonNumberHandlingAttribute) attribute. -Custom converters can be implemented for types that don't have built-in support. +If you're using `System.Text.Json` indirectly by using ASP.NET Core, you don't need to do anything to get behavior like `Newtonsoft.Json`. ASP.NET Core specifies [web defaults](system-text-json-how-to.md#web-defaults-for-jsonserializeroptions) when it uses `System.Text.Json`, and web defaults allow quoted numbers. -### Quoted numbers +For more information, see [Allow or write numbers in quotes](system-text-json-how-to.md#allow-or-write-numbers-in-quotes). +::: zone-end -`Newtonsoft.Json` can serialize or deserialize numbers represented by JSON strings (surrounded by quotes). For example, it can accept: `{"DegreesCelsius":"23"}` instead of `{"DegreesCelsius":23}`. To enable that behavior in , implement a custom converter like the following example. The converter handles properties defined as `long`: +::: zone pivot="dotnet-core-3-1" +`Newtonsoft.Json` can serialize or deserialize numbers represented by JSON strings (surrounded by quotes). For example, it can accept: `{"DegreesCelsius":"23"}` instead of `{"DegreesCelsius":23}`. To enable that behavior in in .NET Core 3.1, implement a custom converter like the following example. The converter handles properties defined as `long`: * It serializes them as JSON strings. * It accepts JSON numbers and numbers within quotes while deserializing. @@ -201,12 +243,147 @@ Custom converters can be implemented for types that don't have built-in support. [!code-csharp[](snippets/system-text-json-how-to/csharp/LongToStringConverter.cs)] Register this custom converter by [using an attribute](system-text-json-converters-how-to.md#registration-sample---jsonconverter-on-a-property) on individual `long` properties or by [adding the converter](system-text-json-converters-how-to.md#registration-sample---converters-collection) to the collection. +::: zone-end + +### Specify constructor to use when deserializing + +The `Newtonsoft.Json` `[JsonConstructor]` attribute lets you specify which constructor to call when deserializing to a POCO. + +::: zone pivot="dotnet-5-0" +`System.Text.Json` also has a [[JsonConstructor]](xref:System.Text.Json.Serialization.JsonConstructorAttribute) attribute. For more information, see [Immutable types and Records](system-text-json-how-to.md#immutable-types-and-records). +::: zone-end + +::: zone pivot="dotnet-core-3-1" + supports only parameterless constructors. As a workaround, you can call whichever constructor you need in a custom converter. See the example for [Deserialize to immutable classes and structs](#deserialize-to-immutable-classes-and-structs). +::: zone-end + +### Conditionally ignore a property + +`Newtonsoft.Json` has several ways to conditionally ignore a property on serialization or deserialization: + +* `DefaultContractResolver` lets you select properties to include or ignore, based on arbitrary criteria. +* The `NullValueHandling` and `DefaultValueHandling` settings on `JsonSerializerSettings` let you specify that all null-value or default-value properties should be ignored. +* The `NullValueHandling` and `DefaultValueHandling` settings on the `[JsonProperty]` attribute let you specify individual properties that should be ignored when set to null or the default value. + +::: zone pivot="dotnet-5-0" + + provides the following ways to ignore properties or fields while serializing: + +* The [[JsonIgnore]](system-text-json-how-to.md#ignore-individual-properties) attribute on a property causes the property to be omitted from the JSON during serialization. +* The [IgnoreReadOnlyProperties](system-text-json-how-to.md#ignore-all-read-only-properties) global option lets you ignore all read-only properties. +* If you're [Including fields](system-text-json-how-to.md#include-fields), the global option lets you ignore all read-only fields. +* The `DefaultIgnoreCondition` global option lets you [ignore all value type properties that have default values](system-text-json-how-to.md#ignore-all-default-value-properties), or [ignore all reference type properties that have null values](system-text-json-how-to.md#ignore-all-null-value-properties). + +::: zone-end + +::: zone pivot="dotnet-core-3-1" + + provides the following ways to ignore properties while serializing: + +* The [[JsonIgnore]](system-text-json-how-to.md#ignore-individual-properties) attribute on a property causes the property to be omitted from the JSON during serialization. +* The [IgnoreNullValues](system-text-json-how-to.md#ignore-all-null-value-properties) global option lets you ignore all null-value properties. +* The [IgnoreReadOnlyProperties](system-text-json-how-to.md#ignore-all-read-only-properties) global option lets you ignore all read-only properties. +::: zone-end + +These options **don't** let you: + +::: zone pivot="dotnet-5-0" + +* Ignore selected properties based on arbitrary criteria evaluated at run time. + +::: zone-end + +::: zone pivot="dotnet-core-3-1" + +* Ignore all properties that have the default value for the type. +* Ignore selected properties that have the default value for the type. +* Ignore selected properties if their value is null. +* Ignore selected properties based on arbitrary criteria evaluated at run time. + +::: zone-end + +For that functionality, you can write a custom converter. Here's a sample POCO and a custom converter for it that illustrates this approach: + +[!code-csharp[](snippets/system-text-json-how-to/csharp/WeatherForecast.cs?name=SnippetWF)] + +[!code-csharp[](snippets/system-text-json-how-to/csharp/WeatherForecastRuntimeIgnoreConverter.cs)] + +The converter causes the `Summary` property to be omitted from serialization if its value is null, an empty string, or "N/A". + +Register this custom converter by [using an attribute on the class](system-text-json-converters-how-to.md#registration-sample---jsonconverter-on-a-type) or by [adding the converter](system-text-json-converters-how-to.md#registration-sample---converters-collection) to the collection. + +This approach requires additional logic if: + +* The POCO includes complex properties. +* You need to handle attributes such as `[JsonIgnore]` or options such as custom encoders. + +### Public and non-public fields + +`Newtonsoft.Json` can serialize and deserialize fields as well as properties. + +::: zone pivot="dotnet-5-0" +In , use the global setting or the [[JsonInclude]](xref:System.Text.Json.Serialization.JsonIncludeAttribute) attribute to include public fields when serializing or deserializing. For an example, see [Include fields](system-text-json-how-to.md#include-fields). +::: zone-end + +::: zone pivot="dotnet-core-3-1" + in .NET Core 3.1 only works with public properties. Custom converters can provide this functionality. +::: zone-end + +### Preserve object references and handle loops + +By default, `Newtonsoft.Json` serializes by value. For example, if an object contains two properties that contain a reference to the same `Person` object, the values of that `Person` object's properties are duplicated in the JSON. + +`Newtonsoft.Json` has a `PreserveReferencesHandling` setting on `JsonSerializerSettings` that lets you serialize by reference: + +* An identifier metadata is added to the JSON created for the first `Person` object. +* The JSON that is created for the second `Person` object contains a reference to that identifier instead of property values. + +`Newtonsoft.Json` also has a `ReferenceLoopHandling` setting that lets you ignore circular references rather than throw an exception. + +::: zone pivot="dotnet-5-0" +To preserve references and handle circular references in , set to . The `ReferenceHandler.Preserve` setting is equivalent to `PreserveReferencesHandling` = `PreserveReferencesHandling.All` in `Newtonsoft.Json`. + +Like the Newtonsoft.Json [ReferenceResolver](https://www.newtonsoft.com/json/help/html/P_Newtonsoft_Json_JsonSerializer_ReferenceResolver.htm), the class defines the behavior of preserving references on serialization and deserialization. Create a derived class to specify custom behavior. For an example, see [GuidReferenceResolver](https://github.com/dotnet/dotnet-docs/blob/master/docs/standard/serialization/snippets/system-text-json-how-to-5-0/csharp/GuidReferenceResolver.cs).. + +Some related `Newtonsoft.Json` features are not supported: + +* [JsonPropertyAttribute.IsReference](https://www.newtonsoft.com/json/help/html/P_Newtonsoft_Json_JsonPropertyAttribute_IsReference.htm) +* [JsonPropertyAttribute.ReferenceLoopHandling](https://www.newtonsoft.com/json/help/html/P_Newtonsoft_Json_JsonPropertyAttribute_ReferenceLoopHandling.htm) + +For more information, see [Preserve references and handle circular references](system-text-json-how-to.md#preserve-references-and-handle-circular-references) +::: zone-end + +::: zone pivot="dotnet-core-3-1" + in .NET Core 3.1 only supports serialization by value and throws an exception for circular references. +::: zone-end ### Dictionary with non-string key -`Newtonsoft.Json` supports collections of type `Dictionary`. The built-in support for dictionary collections in is limited to `Dictionary`. That is, the key must be a string. +::: zone pivot="dotnet-5-0" +Both `Newtonsoft.Json` and `System.Text.Json` support collections of type `Dictionary`. +::: zone-end + +::: zone pivot="dotnet-core-3-1" +`Newtonsoft.Json` supports collections of type `Dictionary`. The built-in support for dictionary collections in in .NET Core 3.1 is limited to `Dictionary`. That is, the key must be a string. To support a dictionary with an integer or some other type as the key, create a converter like the example in [How to write custom converters](system-text-json-converters-how-to.md#support-dictionary-with-non-string-key). +::: zone-end + +### Types without built-in support + + doesn't provide built-in support for the following types: + +* and related types +* F# types, such as [discriminated unions](../../fsharp/language-reference/discriminated-unions.md), [record types](../../fsharp/language-reference/records.md), and [anonymous record types](../../fsharp/language-reference/anonymous-records.md). +* +* +* +* +* +* +* and its associated generic types + +Custom converters can be implemented for types that don't have built-in support. ### Polymorphic serialization @@ -246,7 +423,7 @@ To implement type inference for `object` properties, create a converter like the * `NullValueHandling` is set to `Ignore`, and * During deserialization, the JSON contains a null value for a non-nullable value type. -In the same scenario, does throw an exception. (The corresponding null handling setting is .) +In the same scenario, does throw an exception. (The corresponding null-handling setting in `System.Text.Json` is = `true`.) If you own the target type, the best workaround is to make the property in question nullable (for example, change `int` to `int?`). @@ -274,7 +451,14 @@ After deserialization, the `Date` property has 1/1/0001 (`default(DateTimeOffset ### Deserialize to immutable classes and structs -`Newtonsoft.Json` can deserialize to immutable classes and structs because it can use constructors that have parameters. supports only public parameterless constructors. As a workaround, you can call a constructor with parameters in a custom converter. +`Newtonsoft.Json` can deserialize to immutable classes and structs because it can use constructors that have parameters. + +::: zone pivot="dotnet-5-0" +In , use the [[JsonConstructor]](xref:System.Text.Json.Serialization.JsonConstructorAttribute) attribute to specify use of a parameterized constructor. Records in C# 9 are also immutable and are supported as deserialization targets. For more information, see [Immutable types and Records](system-text-json-how-to.md#immutable-types-and-records). +::: zone-end + +::: zone pivot="dotnet-core-3-1" + in .NET Core 3.1 supports only public parameterless constructors. As a workaround, you can call a constructor with parameters in a custom converter. Here's an immutable struct with multiple constructor parameters: @@ -287,10 +471,7 @@ And here's a converter that serializes and deserializes this struct: Register this custom converter by [adding the converter](system-text-json-converters-how-to.md#registration-sample---converters-collection) to the collection. For an example of a similar converter that handles open generic properties, see the [built-in converter for key-value pairs](https://github.com/dotnet/runtime/blob/81bf79fd9aa75305e55abe2f7e9ef3f60624a3a1/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/JsonValueConverterKeyValuePair.cs). - -### Specify constructor to use - -The `Newtonsoft.Json` `[JsonConstructor]` attribute lets you specify which constructor to call when deserializing to a POCO. supports only parameterless constructors. As a workaround, you can call whichever constructor you need in a custom converter. See the example for [Deserialize to immutable classes and structs](#deserialize-to-immutable-classes-and-structs). +::: zone-end ### Required properties @@ -339,42 +520,6 @@ The required properties converter would require additional logic if you need to * A property for a non-nullable type is present in the JSON, but the value is the default for the type, such as zero for an `int`. * A property for a nullable value type is present in the JSON, but the value is null. -### Conditionally ignore a property - -`Newtonsoft.Json` has several ways to conditionally ignore a property on serialization or deserialization: - -* `DefaultContractResolver` lets you select properties to include or exclude, based on arbitrary criteria. -* The `NullValueHandling` and `DefaultValueHandling` settings on `JsonSerializerSettings` let you specify that all null-value or default-value properties should be ignored. -* The `NullValueHandling` and `DefaultValueHandling` settings on the `[JsonProperty]` attribute let you specify individual properties that should be ignored when set to null or the default value. - - provides the following ways to omit properties while serializing: - -* The [[JsonIgnore]](system-text-json-how-to.md#exclude-individual-properties) attribute on a property causes the property to be omitted from the JSON during serialization. -* The [IgnoreNullValues](system-text-json-how-to.md#exclude-all-null-value-properties) global option lets you exclude all null-value properties. -* The [IgnoreReadOnlyProperties](system-text-json-how-to.md#exclude-all-read-only-properties) global option lets you exclude all read-only properties. - -These options **don't** let you: - -* Ignore all properties that have the default value for the type. -* Ignore selected properties that have the default value for the type. -* Ignore selected properties if their value is null. -* Ignore selected properties based on arbitrary criteria evaluated at run time. - -For that functionality, you can write a custom converter. Here's a sample POCO and a custom converter for it that illustrates this approach: - -[!code-csharp[](snippets/system-text-json-how-to/csharp/WeatherForecast.cs?name=SnippetWF)] - -[!code-csharp[](snippets/system-text-json-how-to/csharp/WeatherForecastRuntimeIgnoreConverter.cs)] - -The converter causes the `Summary` property to be omitted from serialization if its value is null, an empty string, or "N/A". - -Register this custom converter by [using an attribute on the class](system-text-json-converters-how-to.md#registration-sample---jsonconverter-on-a-type) or by [adding the converter](system-text-json-converters-how-to.md#registration-sample---converters-collection) to the collection. - -This approach requires additional logic if: - -* The POCO includes complex properties. -* You need to handle attributes such as `[JsonIgnore]` or options such as custom encoders. - ### Specify date format `Newtonsoft.Json` provides several ways to control how properties of `DateTime` and `DateTimeOffset` types are serialized and deserialized: @@ -406,13 +551,17 @@ If you use a custom converter that follows the preceding sample: For more information about custom converters that recursively call `Serialize` or `Deserialize`, see the [Required properties](#required-properties) section earlier in this article. -### Public and non-public fields +### Non-public property setters and getters -`Newtonsoft.Json` can serialize and deserialize fields as well as properties. only works with public properties. Custom converters can provide this functionality. +`Newtonsoft.Json` can use private and internal property setters and getters via the `JsonProperty` attribute. -### Internal and private property setters and getters +::: zone pivot="dotnet-5-0" + supports private and internal property setters and getters via the [[JsonInclude]](xref:System.Text.Json.Serialization.JsonIncludeAttribute) attribute. For sample code, see [Non-public property accessors](system-text-json-how-to.md#non-public-property-accessors). +::: zone-end -`Newtonsoft.Json` can use private and internal property setters and getters via the `JsonProperty` attribute. supports only public setters. Custom converters can provide this functionality. +::: zone pivot="dotnet-core-3-1" + in .NET Core 3.1 supports only public setters. Custom converters can provide this functionality. +::: zone-end ### Populate existing objects @@ -426,23 +575,6 @@ The `Newtonsoft.Json` `ObjectCreationHandling` setting lets you specify that obj During deserialization, `Newtonsoft.Json` adds objects to a collection even if the property has no setter. ignores properties that don't have setters. Custom converters can provide this functionality. -## Scenarios that JsonSerializer currently doesn't support - -For the following scenarios, workarounds are not practical or possible. If you rely on these `Newtonsoft.Json` features, migration will not be possible without significant changes. - -### Preserve object references and handle loops - -By default, `Newtonsoft.Json` serializes by value. For example, if an object contains two properties that contain a reference to the same `Person` object, the values of that `Person` object's properties are duplicated in the JSON. - -`Newtonsoft.Json` has a `PreserveReferencesHandling` setting on `JsonSerializerSettings` that lets you serialize by reference: - -* An identifier metadata is added to the JSON created for the first `Person` object. -* The JSON that is created for the second `Person` object contains a reference to that identifier instead of property values. - -`Newtonsoft.Json` also has a `ReferenceLoopHandling` setting that lets you ignore circular references rather than throw an exception. - - only supports serialization by value and throws an exception for circular references. - ### System.Runtime.Serialization attributes doesn't support attributes from the `System.Runtime.Serialization` namespace, such as `DataMemberAttribute` and `IgnoreDataMemberAttribute`. diff --git a/docs/zone-pivot-groups.yml b/docs/zone-pivot-groups.yml index 299072bd77eeb..e7b5fbf230b79 100644 --- a/docs/zone-pivot-groups.yml +++ b/docs/zone-pivot-groups.yml @@ -19,4 +19,12 @@ groups: - id: xunit title: xUnit - id: nunit - title: NUnit \ No newline at end of file + title: NUnit +- id: dotnet-version + title: .NET version + prompt: Choose a .NET version + pivots: + - id: dotnet-5-0 + title: .NET 5.0 + - id: dotnet-core-3-1 + title: .NET Core 3.1