From 139f8bd3ddcee1168f66e1ca241328d6ed0f2590 Mon Sep 17 00:00:00 2001 From: Tom Dykstra Date: Thu, 21 Nov 2019 20:00:27 -0800 Subject: [PATCH 01/10] callbacks --- .../core/system-text-json/csharp/Callbacks.cs | 33 ++++++++ .../core/system-text-json/csharp/Program.cs | 3 + .../csharp/WeatherForecastConverter.cs | 81 +++++++++++++++++++ 3 files changed, 117 insertions(+) create mode 100644 snippets/core/system-text-json/csharp/Callbacks.cs create mode 100644 snippets/core/system-text-json/csharp/WeatherForecastConverter.cs diff --git a/snippets/core/system-text-json/csharp/Callbacks.cs b/snippets/core/system-text-json/csharp/Callbacks.cs new file mode 100644 index 00000000000..c0bd73835fd --- /dev/null +++ b/snippets/core/system-text-json/csharp/Callbacks.cs @@ -0,0 +1,33 @@ +using System; +using System.Collections.Generic; +using System.Text.Json; +using System.Text.Json.Serialization; + +namespace SystemTextJsonSamples +{ + public class Callbacks + { + public static void Run() + { + string jsonString; + var wf = WeatherForecastFactories.CreateWeatherForecast(); + wf.DisplayPropertyValues(); + + // + var serializeOptions = new JsonSerializerOptions(); + serializeOptions.Converters.Add(new WeatherForecastConverter()); + // + serializeOptions.WriteIndented = true; + jsonString = JsonSerializer.Serialize(wf, serializeOptions); + Console.WriteLine($"JSON output:\n{jsonString}\n"); + jsonString = @"{""Date"": null,""TemperatureCelsius"": 25,""Summary"":""Hot""}"; + // + var deserializeOptions = new JsonSerializerOptions(); + deserializeOptions.Converters.Add(new WeatherForecastConverter()); + wf = JsonSerializer.Deserialize(jsonString, deserializeOptions); + // + wf.DisplayPropertyValues(); + } + } + +} diff --git a/snippets/core/system-text-json/csharp/Program.cs b/snippets/core/system-text-json/csharp/Program.cs index a36a5f33246..48657d1b7d5 100644 --- a/snippets/core/system-text-json/csharp/Program.cs +++ b/snippets/core/system-text-json/csharp/Program.cs @@ -79,6 +79,9 @@ static async Task Main(string[] args) Console.WriteLine("\n============================= Custom converter inferred types to Object\n"); ConvertInferredTypesToObject.Run(); + Console.WriteLine("\n============================= Callbacks\n"); + Callbacks.Run(); + Console.WriteLine("\n============================= JsonDocument data access\n"); JsonDocumentDataAccess.Run(); diff --git a/snippets/core/system-text-json/csharp/WeatherForecastConverter.cs b/snippets/core/system-text-json/csharp/WeatherForecastConverter.cs new file mode 100644 index 00000000000..b78bdbb774c --- /dev/null +++ b/snippets/core/system-text-json/csharp/WeatherForecastConverter.cs @@ -0,0 +1,81 @@ +using System; +using System.Text.Json; +using System.Text.Json.Serialization; + +namespace SystemTextJsonSamples +{ + public class WeatherForecastConverter : JsonConverter + { + public override WeatherForecast Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) + { + Console.WriteLine("OnDeserializing"); + if (reader.TokenType != JsonTokenType.StartObject) + { + throw new JsonException(); + } + + var wf = new WeatherForecast(); + + while (reader.Read()) + { + if (reader.TokenType == JsonTokenType.EndObject) + { + Console.WriteLine("OnDeserialized"); + return wf; + } + + if (reader.TokenType == JsonTokenType.PropertyName) + { + var propertyName = reader.GetString(); + reader.Read(); + switch (propertyName) + { + case "Date": + if (reader.TokenType == JsonTokenType.Null) + { + wf.Date = DateTimeOffset.MinValue; + } + else + { + DateTimeOffset date = reader.GetDateTimeOffset(); + wf.Date = date; + } + break; + case "TemperatureCelsius": + int temperatureCelsius = reader.GetInt32(); + wf.TemperatureCelsius = temperatureCelsius; + break; + case "Summary": + string summary = reader.GetString(); + if (wf.TemperatureCelsius != 0) + { + wf.Summary = summary; + } + break; + } + } + } + + throw new JsonException(); + } + + public override void Write(Utf8JsonWriter writer, WeatherForecast wf, JsonSerializerOptions options) + { + Console.WriteLine("OnSerializing"); + + writer.WriteStartObject(); + + writer.WriteString("Date", wf.Date); + writer.WriteNumber("TemperatureCelsius", wf.TemperatureCelsius); + if (wf.TemperatureCelsius != 0) + { + writer.WriteString("Summary", wf.Summary); + } + + writer.WriteEndObject(); + + Console.WriteLine("OnSerialized"); + + } + } +} From cef5c76c0346a18fda0bdcd23417211beecd7b37 Mon Sep 17 00:00:00 2001 From: Tom Dykstra Date: Fri, 22 Nov 2019 10:25:01 -0800 Subject: [PATCH 02/10] misc --- .../core/system-text-json/csharp/Callbacks.cs | 15 +++++++- .../ConvertInferredTypesToObject - Copy.cs | 35 +++++++++++++++++++ .../csharp/ConvertInferredTypesToObject.cs | 20 +++-------- .../csharp/LongToStringConverter.cs | 31 ++++++++++++++++ .../core/system-text-json/csharp/Program.cs | 3 ++ .../csharp/RoundtripToFileAsync.cs | 19 ++++++++++ .../csharp/Utf8ReaderFromFile.cs | 5 +++ .../csharp/WeatherForecast.cs | 25 +++++++++++++ .../csharp/WeatherForecastConverter.cs | 14 ++++++-- 9 files changed, 149 insertions(+), 18 deletions(-) create mode 100644 snippets/core/system-text-json/csharp/ConvertInferredTypesToObject - Copy.cs create mode 100644 snippets/core/system-text-json/csharp/LongToStringConverter.cs diff --git a/snippets/core/system-text-json/csharp/Callbacks.cs b/snippets/core/system-text-json/csharp/Callbacks.cs index c0bd73835fd..559ab894b32 100644 --- a/snippets/core/system-text-json/csharp/Callbacks.cs +++ b/snippets/core/system-text-json/csharp/Callbacks.cs @@ -20,11 +20,24 @@ public static void Run() serializeOptions.WriteIndented = true; jsonString = JsonSerializer.Serialize(wf, serializeOptions); Console.WriteLine($"JSON output:\n{jsonString}\n"); - jsonString = @"{""Date"": null,""TemperatureCelsius"": 25,""Summary"":""Hot""}"; // + jsonString = @"{""Date"": null,""TemperatureCelsius"": 25,""Summary"":""Hot""}"; var deserializeOptions = new JsonSerializerOptions(); deserializeOptions.Converters.Add(new WeatherForecastConverter()); wf = JsonSerializer.Deserialize(jsonString, deserializeOptions); + wf.DisplayPropertyValues(); + + jsonString = @"{""TemperatureCelsius"": 25,""Summary"":""Hot""}"; + deserializeOptions = new JsonSerializerOptions(); + deserializeOptions.Converters.Add(new WeatherForecastConverter()); + try + { + wf = JsonSerializer.Deserialize(jsonString, deserializeOptions); + } + catch (JsonException ex) + { + Console.WriteLine($"{ex.Message} Path={ex.Path}"); + } // wf.DisplayPropertyValues(); } diff --git a/snippets/core/system-text-json/csharp/ConvertInferredTypesToObject - Copy.cs b/snippets/core/system-text-json/csharp/ConvertInferredTypesToObject - Copy.cs new file mode 100644 index 00000000000..efa2f7669e4 --- /dev/null +++ b/snippets/core/system-text-json/csharp/ConvertInferredTypesToObject - Copy.cs @@ -0,0 +1,35 @@ +using System; +using System.Text.Json; + +namespace SystemTextJsonSamples +{ + public class ConvertInferredTypesToObject + { + public static void Run() + { + string jsonString; + + // Serialize to create input JSON + var weatherForecast = WeatherForecastFactories.CreateWeatherForecast(); + var serializeOptions = new JsonSerializerOptions + { + WriteIndented = true + }; + serializeOptions.WriteIndented = true; + jsonString = JsonSerializer.Serialize(weatherForecast, serializeOptions); + Console.WriteLine($"JSON input:\n{jsonString}\n"); + + // Deserialize without converter + // Properties are JsonElement type. + WeatherForecastWithObjectProperties weatherForecastWithObjectProperties = JsonSerializer.Deserialize(jsonString); + weatherForecastWithObjectProperties.DisplayPropertyValues(); + + // + var deserializeOptions = new JsonSerializerOptions(); + deserializeOptions.Converters.Add(new ObjectToInferredTypesConverter()); + // + weatherForecastWithObjectProperties = JsonSerializer.Deserialize(jsonString, deserializeOptions); + weatherForecastWithObjectProperties.DisplayPropertyValues(); + } + } +} diff --git a/snippets/core/system-text-json/csharp/ConvertInferredTypesToObject.cs b/snippets/core/system-text-json/csharp/ConvertInferredTypesToObject.cs index efa2f7669e4..6085319e563 100644 --- a/snippets/core/system-text-json/csharp/ConvertInferredTypesToObject.cs +++ b/snippets/core/system-text-json/csharp/ConvertInferredTypesToObject.cs @@ -3,33 +3,23 @@ namespace SystemTextJsonSamples { - public class ConvertInferredTypesToObject + public class ConvertLongToString { public static void Run() { string jsonString; // Serialize to create input JSON - var weatherForecast = WeatherForecastFactories.CreateWeatherForecast(); + var weatherForecast = WeatherForecastFactories.CreateWeatherForecastWithLong(); var serializeOptions = new JsonSerializerOptions { WriteIndented = true }; - serializeOptions.WriteIndented = true; jsonString = JsonSerializer.Serialize(weatherForecast, serializeOptions); - Console.WriteLine($"JSON input:\n{jsonString}\n"); + Console.WriteLine($"JSON output:\n{jsonString}\n"); - // Deserialize without converter - // Properties are JsonElement type. - WeatherForecastWithObjectProperties weatherForecastWithObjectProperties = JsonSerializer.Deserialize(jsonString); - weatherForecastWithObjectProperties.DisplayPropertyValues(); - - // - var deserializeOptions = new JsonSerializerOptions(); - deserializeOptions.Converters.Add(new ObjectToInferredTypesConverter()); - // - weatherForecastWithObjectProperties = JsonSerializer.Deserialize(jsonString, deserializeOptions); - weatherForecastWithObjectProperties.DisplayPropertyValues(); + weatherForecast = JsonSerializer.Deserialize(jsonString); + weatherForecast.DisplayPropertyValues(); } } } diff --git a/snippets/core/system-text-json/csharp/LongToStringConverter.cs b/snippets/core/system-text-json/csharp/LongToStringConverter.cs new file mode 100644 index 00000000000..e671104602c --- /dev/null +++ b/snippets/core/system-text-json/csharp/LongToStringConverter.cs @@ -0,0 +1,31 @@ +using System; +using System.Buffers; +using System.Buffers.Text; +using System.Text.Json; +using System.Text.Json.Serialization; + +namespace SystemTextJsonSamples +{ + public class LongToStringConverter : JsonConverter + { + public override long Read(ref Utf8JsonReader reader, Type type, JsonSerializerOptions options) + { + if (reader.TokenType == JsonTokenType.String) + { + ReadOnlySpan span = reader.HasValueSequence ? reader.ValueSequence.ToArray() : reader.ValueSpan; + if (Utf8Parser.TryParse(span, out long number, out int bytesConsumed) && span.Length == bytesConsumed) + return number; + + if (Int64.TryParse(reader.GetString(), out number)) + return number; + } + + return reader.GetInt64(); + } + + public override void Write(Utf8JsonWriter writer, long value, JsonSerializerOptions options) + { + writer.WriteStringValue(value.ToString()); + } + } +} diff --git a/snippets/core/system-text-json/csharp/Program.cs b/snippets/core/system-text-json/csharp/Program.cs index 48657d1b7d5..a84618ee9cd 100644 --- a/snippets/core/system-text-json/csharp/Program.cs +++ b/snippets/core/system-text-json/csharp/Program.cs @@ -79,6 +79,9 @@ static async Task Main(string[] args) Console.WriteLine("\n============================= Custom converter inferred types to Object\n"); ConvertInferredTypesToObject.Run(); + Console.WriteLine("\n============================= Custom converter long to string\n"); + ConvertLongToString.Run(); + Console.WriteLine("\n============================= Callbacks\n"); Callbacks.Run(); diff --git a/snippets/core/system-text-json/csharp/RoundtripToFileAsync.cs b/snippets/core/system-text-json/csharp/RoundtripToFileAsync.cs index 21581672631..f46e5d2dde2 100644 --- a/snippets/core/system-text-json/csharp/RoundtripToFileAsync.cs +++ b/snippets/core/system-text-json/csharp/RoundtripToFileAsync.cs @@ -11,6 +11,7 @@ class RoundtripToFileAsync public static async Task RunAsync() { string fileName = "WeatherForecastAsync.json"; + string fileNameUtf8 = "WeatherForecastAsyncUtf8"; var weatherForecast = WeatherForecastFactories.CreateWeatherForecast(); weatherForecast.DisplayPropertyValues(); @@ -29,6 +30,24 @@ public static async Task RunAsync() } // weatherForecast.DisplayPropertyValues(); + + using (FileStream fs = File.Create(fileNameUtf8)) + { + using (StreamWriter writer = new StreamWriter(fs, Encoding.UTF8)) + { + await JsonSerializer.SerializeAsync(fs, weatherForecast); + } + } + Console.WriteLine($"The result is in {fileNameUtf8}\n"); + using (FileStream fs = File.OpenRead(fileNameUtf8)) + { + //System.Text.Json.JsonException: '0xEF' is invalid after a single JSON value. Expected end of data. + //weatherForecast = await JsonSerializer.DeserializeAsync(fs); + } + // + weatherForecast.DisplayPropertyValues(); + + } } } diff --git a/snippets/core/system-text-json/csharp/Utf8ReaderFromFile.cs b/snippets/core/system-text-json/csharp/Utf8ReaderFromFile.cs index 1eea31b8757..b5855f66993 100644 --- a/snippets/core/system-text-json/csharp/Utf8ReaderFromFile.cs +++ b/snippets/core/system-text-json/csharp/Utf8ReaderFromFile.cs @@ -11,11 +11,16 @@ class Utf8ReaderFromFile public static void Run() { // Read as UTF-16 and transcode to UTF-8 to convert to a Span + //***The following code also works with the Utf8 file. + string fileName = "Universities.json"; string jsonString = File.ReadAllText(fileName); ReadOnlySpan jsonReadOnlySpan = Encoding.UTF8.GetBytes(jsonString); // Or ReadAllBytes if the file encoding is UTF-8: + //***But this code doesn't work with the UTF-16 file + //System.Text.Json.JsonReaderException: '0xEF' is an invalid start of a value. LineNumber: 0 | BytePositionInLine: 0. + //string fileName = "UniversitiesUtf8.json"; //ReadOnlySpan jsonReadOnlySpan = File.ReadAllBytes(fileName); diff --git a/snippets/core/system-text-json/csharp/WeatherForecast.cs b/snippets/core/system-text-json/csharp/WeatherForecast.cs index afdb75c9c13..3920762bed3 100644 --- a/snippets/core/system-text-json/csharp/WeatherForecast.cs +++ b/snippets/core/system-text-json/csharp/WeatherForecast.cs @@ -13,6 +13,14 @@ public class WeatherForecast } // + public class WeatherForecastWithLong + { + public DateTimeOffset Date { get; set; } + [JsonConverter(typeof(LongToStringConverter))] + public long TemperatureCelsius { get; set; } + public string Summary { get; set; } + } + // public class WeatherForecastWithDefault { @@ -171,6 +179,12 @@ public static void DisplayPropertyValues(this WeatherForecast wf) Console.WriteLine(); } + public static void DisplayPropertyValues(this WeatherForecastWithLong wf) + { + Utilities.DisplayPropertyValues(wf); + Console.WriteLine(); + } + public static void DisplayPropertyValues(this WeatherForecastWithDefault wf) { Utilities.DisplayPropertyValues(wf); @@ -289,6 +303,17 @@ public static WeatherForecast CreateWeatherForecast() return weatherForecast; } + public static WeatherForecastWithLong CreateWeatherForecastWithLong() + { + var weatherForecast = new WeatherForecastWithLong + { + Date = DateTime.Parse("2019-08-01"), + TemperatureCelsius = 25, + Summary = "Hot" + }; + return weatherForecast; + } + public static WeatherForecastWithROProperty CreateWeatherForecastWithROProperty() { var weatherForecast = new WeatherForecastWithROProperty diff --git a/snippets/core/system-text-json/csharp/WeatherForecastConverter.cs b/snippets/core/system-text-json/csharp/WeatherForecastConverter.cs index b78bdbb774c..e44fc63d4f5 100644 --- a/snippets/core/system-text-json/csharp/WeatherForecastConverter.cs +++ b/snippets/core/system-text-json/csharp/WeatherForecastConverter.cs @@ -8,6 +8,7 @@ public class WeatherForecastConverter : JsonConverter { public override WeatherForecast Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) { + // Location for OnDeserializing "callback" code. Console.WriteLine("OnDeserializing"); if (reader.TokenType != JsonTokenType.StartObject) { @@ -20,7 +21,13 @@ public override WeatherForecast Read(ref Utf8JsonReader reader, Type typeToConve { if (reader.TokenType == JsonTokenType.EndObject) { + // Location for OnDeserialized "callback" code. Console.WriteLine("OnDeserialized"); + // Check for required fields set by values in JSON + if (wf.Date == default(DateTimeOffset)) + { + throw new JsonException("Required property not received in the JSON"); + }; return wf; } @@ -31,9 +38,10 @@ public override WeatherForecast Read(ref Utf8JsonReader reader, Type typeToConve switch (propertyName) { case "Date": + // Avoid exception on getting null in JSON for a non-null value type. if (reader.TokenType == JsonTokenType.Null) { - wf.Date = DateTimeOffset.MinValue; + wf.Date = DateTimeOffset.Now; } else { @@ -47,6 +55,7 @@ public override WeatherForecast Read(ref Utf8JsonReader reader, Type typeToConve break; case "Summary": string summary = reader.GetString(); + // Ignore properties in JSON based on criteria evaluated at runtime. if (wf.TemperatureCelsius != 0) { wf.Summary = summary; @@ -61,6 +70,7 @@ public override WeatherForecast Read(ref Utf8JsonReader reader, Type typeToConve public override void Write(Utf8JsonWriter writer, WeatherForecast wf, JsonSerializerOptions options) { + // Location for OnSerializing "callback" code. Console.WriteLine("OnSerializing"); writer.WriteStartObject(); @@ -74,8 +84,8 @@ public override void Write(Utf8JsonWriter writer, WeatherForecast wf, JsonSerial writer.WriteEndObject(); + // Location for Onserialized "callback" code. Console.WriteLine("OnSerialized"); - } } } From ff7158a42deabdd5ec2919883823575502018b57 Mon Sep 17 00:00:00 2001 From: Tom Dykstra Date: Mon, 25 Nov 2019 14:01:00 -0800 Subject: [PATCH 03/10] misc --- .../ConvertInferredTypesToObject - Copy.cs | 35 ------------------- .../csharp/ConvertInferredTypesToObject.cs | 20 ++++++++--- .../csharp/ConvertLongToString.cs | 25 +++++++++++++ 3 files changed, 40 insertions(+), 40 deletions(-) delete mode 100644 snippets/core/system-text-json/csharp/ConvertInferredTypesToObject - Copy.cs create mode 100644 snippets/core/system-text-json/csharp/ConvertLongToString.cs diff --git a/snippets/core/system-text-json/csharp/ConvertInferredTypesToObject - Copy.cs b/snippets/core/system-text-json/csharp/ConvertInferredTypesToObject - Copy.cs deleted file mode 100644 index efa2f7669e4..00000000000 --- a/snippets/core/system-text-json/csharp/ConvertInferredTypesToObject - Copy.cs +++ /dev/null @@ -1,35 +0,0 @@ -using System; -using System.Text.Json; - -namespace SystemTextJsonSamples -{ - public class ConvertInferredTypesToObject - { - public static void Run() - { - string jsonString; - - // Serialize to create input JSON - var weatherForecast = WeatherForecastFactories.CreateWeatherForecast(); - var serializeOptions = new JsonSerializerOptions - { - WriteIndented = true - }; - serializeOptions.WriteIndented = true; - jsonString = JsonSerializer.Serialize(weatherForecast, serializeOptions); - Console.WriteLine($"JSON input:\n{jsonString}\n"); - - // Deserialize without converter - // Properties are JsonElement type. - WeatherForecastWithObjectProperties weatherForecastWithObjectProperties = JsonSerializer.Deserialize(jsonString); - weatherForecastWithObjectProperties.DisplayPropertyValues(); - - // - var deserializeOptions = new JsonSerializerOptions(); - deserializeOptions.Converters.Add(new ObjectToInferredTypesConverter()); - // - weatherForecastWithObjectProperties = JsonSerializer.Deserialize(jsonString, deserializeOptions); - weatherForecastWithObjectProperties.DisplayPropertyValues(); - } - } -} diff --git a/snippets/core/system-text-json/csharp/ConvertInferredTypesToObject.cs b/snippets/core/system-text-json/csharp/ConvertInferredTypesToObject.cs index 6085319e563..efa2f7669e4 100644 --- a/snippets/core/system-text-json/csharp/ConvertInferredTypesToObject.cs +++ b/snippets/core/system-text-json/csharp/ConvertInferredTypesToObject.cs @@ -3,23 +3,33 @@ namespace SystemTextJsonSamples { - public class ConvertLongToString + public class ConvertInferredTypesToObject { public static void Run() { string jsonString; // Serialize to create input JSON - var weatherForecast = WeatherForecastFactories.CreateWeatherForecastWithLong(); + var weatherForecast = WeatherForecastFactories.CreateWeatherForecast(); var serializeOptions = new JsonSerializerOptions { WriteIndented = true }; + serializeOptions.WriteIndented = true; jsonString = JsonSerializer.Serialize(weatherForecast, serializeOptions); - Console.WriteLine($"JSON output:\n{jsonString}\n"); + Console.WriteLine($"JSON input:\n{jsonString}\n"); - weatherForecast = JsonSerializer.Deserialize(jsonString); - weatherForecast.DisplayPropertyValues(); + // Deserialize without converter + // Properties are JsonElement type. + WeatherForecastWithObjectProperties weatherForecastWithObjectProperties = JsonSerializer.Deserialize(jsonString); + weatherForecastWithObjectProperties.DisplayPropertyValues(); + + // + var deserializeOptions = new JsonSerializerOptions(); + deserializeOptions.Converters.Add(new ObjectToInferredTypesConverter()); + // + weatherForecastWithObjectProperties = JsonSerializer.Deserialize(jsonString, deserializeOptions); + weatherForecastWithObjectProperties.DisplayPropertyValues(); } } } diff --git a/snippets/core/system-text-json/csharp/ConvertLongToString.cs b/snippets/core/system-text-json/csharp/ConvertLongToString.cs new file mode 100644 index 00000000000..6085319e563 --- /dev/null +++ b/snippets/core/system-text-json/csharp/ConvertLongToString.cs @@ -0,0 +1,25 @@ +using System; +using System.Text.Json; + +namespace SystemTextJsonSamples +{ + public class ConvertLongToString + { + public static void Run() + { + string jsonString; + + // Serialize to create input JSON + var weatherForecast = WeatherForecastFactories.CreateWeatherForecastWithLong(); + var serializeOptions = new JsonSerializerOptions + { + WriteIndented = true + }; + jsonString = JsonSerializer.Serialize(weatherForecast, serializeOptions); + Console.WriteLine($"JSON output:\n{jsonString}\n"); + + weatherForecast = JsonSerializer.Deserialize(jsonString); + weatherForecast.DisplayPropertyValues(); + } + } +} From acd63d9c3aad5496507d0a0aa209a175d3281b54 Mon Sep 17 00:00:00 2001 From: Tom Dykstra Date: Thu, 5 Dec 2019 15:15:13 -0800 Subject: [PATCH 04/10] callbacks --- .../core/system-text-json/csharp/Callbacks.cs | 26 ++++--------- .../csharp/WeatherForecast.cs | 2 + .../WeatherForecastCallbacksConverter.cs | 37 +++++++++++++++++++ ...verter.cs => WeatherForecastConverterX.cs} | 2 +- 4 files changed, 48 insertions(+), 19 deletions(-) create mode 100644 snippets/core/system-text-json/csharp/WeatherForecastCallbacksConverter.cs rename snippets/core/system-text-json/csharp/{WeatherForecastConverter.cs => WeatherForecastConverterX.cs} (97%) diff --git a/snippets/core/system-text-json/csharp/Callbacks.cs b/snippets/core/system-text-json/csharp/Callbacks.cs index 559ab894b32..c5cce9d3ae6 100644 --- a/snippets/core/system-text-json/csharp/Callbacks.cs +++ b/snippets/core/system-text-json/csharp/Callbacks.cs @@ -13,32 +13,22 @@ public static void Run() var wf = WeatherForecastFactories.CreateWeatherForecast(); wf.DisplayPropertyValues(); - // + // var serializeOptions = new JsonSerializerOptions(); - serializeOptions.Converters.Add(new WeatherForecastConverter()); - // + serializeOptions.Converters.Add(new WeatherForecastCallbacksConverter()); serializeOptions.WriteIndented = true; jsonString = JsonSerializer.Serialize(wf, serializeOptions); + // + Console.WriteLine($"JSON output:\n{jsonString}\n"); + //jsonString = @"{""Date"": null,""TemperatureCelsius"": 25,""Summary"":""Hot""}"; + // - jsonString = @"{""Date"": null,""TemperatureCelsius"": 25,""Summary"":""Hot""}"; var deserializeOptions = new JsonSerializerOptions(); - deserializeOptions.Converters.Add(new WeatherForecastConverter()); + deserializeOptions.Converters.Add(new WeatherForecastCallbacksConverter()); wf = JsonSerializer.Deserialize(jsonString, deserializeOptions); - wf.DisplayPropertyValues(); + // - jsonString = @"{""TemperatureCelsius"": 25,""Summary"":""Hot""}"; - deserializeOptions = new JsonSerializerOptions(); - deserializeOptions.Converters.Add(new WeatherForecastConverter()); - try - { - wf = JsonSerializer.Deserialize(jsonString, deserializeOptions); - } - catch (JsonException ex) - { - Console.WriteLine($"{ex.Message} Path={ex.Path}"); - } - // wf.DisplayPropertyValues(); } } diff --git a/snippets/core/system-text-json/csharp/WeatherForecast.cs b/snippets/core/system-text-json/csharp/WeatherForecast.cs index 3920762bed3..7b9a6771393 100644 --- a/snippets/core/system-text-json/csharp/WeatherForecast.cs +++ b/snippets/core/system-text-json/csharp/WeatherForecast.cs @@ -13,6 +13,7 @@ public class WeatherForecast } // + // public class WeatherForecastWithLong { public DateTimeOffset Date { get; set; } @@ -20,6 +21,7 @@ public class WeatherForecastWithLong public long TemperatureCelsius { get; set; } public string Summary { get; set; } } + // // public class WeatherForecastWithDefault diff --git a/snippets/core/system-text-json/csharp/WeatherForecastCallbacksConverter.cs b/snippets/core/system-text-json/csharp/WeatherForecastCallbacksConverter.cs new file mode 100644 index 00000000000..6cc21c138fb --- /dev/null +++ b/snippets/core/system-text-json/csharp/WeatherForecastCallbacksConverter.cs @@ -0,0 +1,37 @@ +using System; +using System.Text.Json; +using System.Text.Json.Serialization; + +namespace SystemTextJsonSamples +{ + public class WeatherForecastCallbacksConverter : JsonConverter + { + public override WeatherForecast Read( + ref Utf8JsonReader reader, + Type type, + JsonSerializerOptions options) + { + // "before" code needs to go into the POCO constructor; code here doesn't have the POCO instance. + + WeatherForecast value = JsonSerializer.Deserialize(ref reader); // note: "options" not passed in + + // Place "after" code here (e.g. OnDeserialized) + Console.WriteLine("OnDeserialized"); + + return value; + } + + public override void Write( + Utf8JsonWriter writer, + WeatherForecast value, JsonSerializerOptions options) + { + // Place "before" code here (e.g. OnSerializing) + Console.WriteLine("OnSerializing"); + + JsonSerializer.Serialize(writer, value); // note: "options" not passed in + + // Place "after" code here (e.g. OnSerialized) + Console.WriteLine("OnSerialized"); + } + } +} diff --git a/snippets/core/system-text-json/csharp/WeatherForecastConverter.cs b/snippets/core/system-text-json/csharp/WeatherForecastConverterX.cs similarity index 97% rename from snippets/core/system-text-json/csharp/WeatherForecastConverter.cs rename to snippets/core/system-text-json/csharp/WeatherForecastConverterX.cs index e44fc63d4f5..a4ece6972c6 100644 --- a/snippets/core/system-text-json/csharp/WeatherForecastConverter.cs +++ b/snippets/core/system-text-json/csharp/WeatherForecastConverterX.cs @@ -4,7 +4,7 @@ namespace SystemTextJsonSamples { - public class WeatherForecastConverter : JsonConverter + public class WeatherForecastConverterX : JsonConverter { public override WeatherForecast Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) { From 7c9d038011f1d22b192c84b7fff220668459f3bc Mon Sep 17 00:00:00 2001 From: Tom Dykstra Date: Fri, 6 Dec 2019 11:27:50 -0800 Subject: [PATCH 05/10] draft --- .../DateTimeOffsetNullHandlingConverter.cs | 31 ++++++++++++ ...cs => DeserializeInferredTypesToObject.cs} | 2 +- .../DeserializeNullToNonnullableType.cs | 47 +++++++++++++++++ .../csharp/DeserializeRequiredProperty.cs | 50 +++++++++++++++++++ .../core/system-text-json/csharp/Program.cs | 16 ++++-- .../{Callbacks.cs => RoundtripCallbacks.cs} | 2 +- ...s => RoundtripDictionaryTkeyEnumTValue.cs} | 2 +- ...ngToString.cs => RoundtripLongToString.cs} | 2 +- ...Polymorphic.cs => RoundtripPolymorphic.cs} | 2 +- .../WeatherForecastCallbacksConverter.cs | 9 ++-- ...eatherForecastRequiredPropertyConverter.cs | 31 ++++++++++++ 11 files changed, 180 insertions(+), 14 deletions(-) create mode 100644 snippets/core/system-text-json/csharp/DateTimeOffsetNullHandlingConverter.cs rename snippets/core/system-text-json/csharp/{ConvertInferredTypesToObject.cs => DeserializeInferredTypesToObject.cs} (96%) create mode 100644 snippets/core/system-text-json/csharp/DeserializeNullToNonnullableType.cs create mode 100644 snippets/core/system-text-json/csharp/DeserializeRequiredProperty.cs rename snippets/core/system-text-json/csharp/{Callbacks.cs => RoundtripCallbacks.cs} (97%) rename snippets/core/system-text-json/csharp/{ConvertDictionaryTkeyEnumTValue.cs => RoundtripDictionaryTkeyEnumTValue.cs} (95%) rename snippets/core/system-text-json/csharp/{ConvertLongToString.cs => RoundtripLongToString.cs} (94%) rename snippets/core/system-text-json/csharp/{ConvertPolymorphic.cs => RoundtripPolymorphic.cs} (96%) create mode 100644 snippets/core/system-text-json/csharp/WeatherForecastRequiredPropertyConverter.cs diff --git a/snippets/core/system-text-json/csharp/DateTimeOffsetNullHandlingConverter.cs b/snippets/core/system-text-json/csharp/DateTimeOffsetNullHandlingConverter.cs new file mode 100644 index 00000000000..846f600b6b8 --- /dev/null +++ b/snippets/core/system-text-json/csharp/DateTimeOffsetNullHandlingConverter.cs @@ -0,0 +1,31 @@ +using System; +using System.Globalization; +using System.Text.Json; +using System.Text.Json.Serialization; + +namespace SystemTextJsonSamples +{ + public class DateTimeOffsetNullHandlingConverter : JsonConverter + + { + public override DateTimeOffset Read( + ref Utf8JsonReader reader, + Type typeToConvert, + JsonSerializerOptions options) + { + if (reader.TokenType == JsonTokenType.Null) + { + return default; + } + return reader.GetDateTimeOffset(); + } + + public override void Write( + Utf8JsonWriter writer, + DateTimeOffset value, + JsonSerializerOptions options) + { + writer.WriteStringValue(value); + } + } +} diff --git a/snippets/core/system-text-json/csharp/ConvertInferredTypesToObject.cs b/snippets/core/system-text-json/csharp/DeserializeInferredTypesToObject.cs similarity index 96% rename from snippets/core/system-text-json/csharp/ConvertInferredTypesToObject.cs rename to snippets/core/system-text-json/csharp/DeserializeInferredTypesToObject.cs index efa2f7669e4..dc5efced269 100644 --- a/snippets/core/system-text-json/csharp/ConvertInferredTypesToObject.cs +++ b/snippets/core/system-text-json/csharp/DeserializeInferredTypesToObject.cs @@ -3,7 +3,7 @@ namespace SystemTextJsonSamples { - public class ConvertInferredTypesToObject + public class DeserializeInferredTypesToObject { public static void Run() { diff --git a/snippets/core/system-text-json/csharp/DeserializeNullToNonnullableType.cs b/snippets/core/system-text-json/csharp/DeserializeNullToNonnullableType.cs new file mode 100644 index 00000000000..5a65982ced2 --- /dev/null +++ b/snippets/core/system-text-json/csharp/DeserializeNullToNonnullableType.cs @@ -0,0 +1,47 @@ +using System; +using System.Collections.Generic; +using System.Text.Json; +using System.Text.Json.Serialization; + +namespace SystemTextJsonSamples +{ + public class DeserializeNullToNonnullableType + { + public static void Run() + { + string jsonString; + var wf = WeatherForecastFactories.CreateWeatherForecast(); + + var serializeOptions = new JsonSerializerOptions(); + serializeOptions.WriteIndented = true; + serializeOptions.Converters.Add(new DateTimeOffsetNullHandlingConverter()); + jsonString = JsonSerializer.Serialize(wf, serializeOptions); + Console.WriteLine($"JSON with valid Date:\n{jsonString}\n"); + + var deserializeOptions = new JsonSerializerOptions(); + deserializeOptions.Converters.Add(new DateTimeOffsetNullHandlingConverter()); + wf = JsonSerializer.Deserialize(jsonString, deserializeOptions); + wf.DisplayPropertyValues(); + + jsonString = @"{""Date"": null,""TemperatureCelsius"": 25,""Summary"":""Hot""}"; + Console.WriteLine($"JSON with null Date:\n{jsonString}\n"); + + // The missing-date JSON deserializes with error if the converter isn't used. + try + { + wf = JsonSerializer.Deserialize(jsonString); + } + catch (Exception ex) + { + Console.WriteLine($"Exception thrown: {ex.Message}\n"); + } + + Console.WriteLine("Deserialize with converter"); + deserializeOptions = new JsonSerializerOptions(); + deserializeOptions.Converters.Add(new DateTimeOffsetNullHandlingConverter()); + wf = JsonSerializer.Deserialize(jsonString, deserializeOptions); + wf.DisplayPropertyValues(); + } + } + +} diff --git a/snippets/core/system-text-json/csharp/DeserializeRequiredProperty.cs b/snippets/core/system-text-json/csharp/DeserializeRequiredProperty.cs new file mode 100644 index 00000000000..fdb87b2380e --- /dev/null +++ b/snippets/core/system-text-json/csharp/DeserializeRequiredProperty.cs @@ -0,0 +1,50 @@ +using System; +using System.Collections.Generic; +using System.Text.Json; +using System.Text.Json.Serialization; + +namespace SystemTextJsonSamples +{ + public class DeserializeRequiredProperty + { + public static void Run() + { + string jsonString; + var wf = WeatherForecastFactories.CreateWeatherForecast(); + + var serializeOptions = new JsonSerializerOptions(); + serializeOptions.WriteIndented = true; + serializeOptions.Converters.Add(new WeatherForecastRequiredPropertyConverter()); + jsonString = JsonSerializer.Serialize(wf, serializeOptions); + Console.WriteLine($"JSON with Date:\n{jsonString}\n"); + + var deserializeOptions = new JsonSerializerOptions(); + deserializeOptions.Converters.Add(new WeatherForecastRequiredPropertyConverter()); + wf = JsonSerializer.Deserialize(jsonString, deserializeOptions); + wf.DisplayPropertyValues(); + + jsonString = @"{""TemperatureCelsius"": 25,""Summary"":""Hot""}"; + Console.WriteLine($"JSON without Date:\n{jsonString}\n"); + + // The missing-date JSON deserializes without error if the converter isn't used. + Console.WriteLine("Deserialize without converter"); + wf = JsonSerializer.Deserialize(jsonString); + wf.DisplayPropertyValues(); + + Console.WriteLine("Deserialize with converter"); + try + { + deserializeOptions = new JsonSerializerOptions(); + deserializeOptions.Converters.Add(new WeatherForecastRequiredPropertyConverter()); + wf = JsonSerializer.Deserialize(jsonString, deserializeOptions); + } + catch (Exception ex) + { + Console.WriteLine($"Exception thrown: {ex.Message}\n"); + } + // wf object is unchanged if exception is thrown. + wf.DisplayPropertyValues(); + } + } + +} diff --git a/snippets/core/system-text-json/csharp/Program.cs b/snippets/core/system-text-json/csharp/Program.cs index a84618ee9cd..00533ced72a 100644 --- a/snippets/core/system-text-json/csharp/Program.cs +++ b/snippets/core/system-text-json/csharp/Program.cs @@ -71,19 +71,25 @@ static async Task Main(string[] args) RegisterConverterWithAttributeOnType.Run(); Console.WriteLine("\n============================= Custom converter Dictionary with TKey = Enum\n"); - ConvertDictionaryTkeyEnumTValue.Run(); + RoundtripDictionaryTkeyEnumTValue.Run(); Console.WriteLine("\n============================= Custom converter Polymorphic\n"); - ConvertPolymorphic.Run(); + RoundtripPolymorphic.Run(); Console.WriteLine("\n============================= Custom converter inferred types to Object\n"); - ConvertInferredTypesToObject.Run(); + DeserializeInferredTypesToObject.Run(); Console.WriteLine("\n============================= Custom converter long to string\n"); - ConvertLongToString.Run(); + RoundtripLongToString.Run(); Console.WriteLine("\n============================= Callbacks\n"); - Callbacks.Run(); + RoundtripCallbacks.Run(); + + Console.WriteLine("\n============================= Required property\n"); + DeserializeRequiredProperty.Run(); + + Console.WriteLine("\n============================= Null value to nonnullable type\n"); + DeserializeNullToNonnullableType.Run(); Console.WriteLine("\n============================= JsonDocument data access\n"); JsonDocumentDataAccess.Run(); diff --git a/snippets/core/system-text-json/csharp/Callbacks.cs b/snippets/core/system-text-json/csharp/RoundtripCallbacks.cs similarity index 97% rename from snippets/core/system-text-json/csharp/Callbacks.cs rename to snippets/core/system-text-json/csharp/RoundtripCallbacks.cs index c5cce9d3ae6..8e06a2d93f9 100644 --- a/snippets/core/system-text-json/csharp/Callbacks.cs +++ b/snippets/core/system-text-json/csharp/RoundtripCallbacks.cs @@ -5,7 +5,7 @@ namespace SystemTextJsonSamples { - public class Callbacks + public class RoundtripCallbacks { public static void Run() { diff --git a/snippets/core/system-text-json/csharp/ConvertDictionaryTkeyEnumTValue.cs b/snippets/core/system-text-json/csharp/RoundtripDictionaryTkeyEnumTValue.cs similarity index 95% rename from snippets/core/system-text-json/csharp/ConvertDictionaryTkeyEnumTValue.cs rename to snippets/core/system-text-json/csharp/RoundtripDictionaryTkeyEnumTValue.cs index d783a9cc497..851694efb56 100644 --- a/snippets/core/system-text-json/csharp/ConvertDictionaryTkeyEnumTValue.cs +++ b/snippets/core/system-text-json/csharp/RoundtripDictionaryTkeyEnumTValue.cs @@ -3,7 +3,7 @@ namespace SystemTextJsonSamples { - public class ConvertDictionaryTkeyEnumTValue + public class RoundtripDictionaryTkeyEnumTValue { public static void Run() { diff --git a/snippets/core/system-text-json/csharp/ConvertLongToString.cs b/snippets/core/system-text-json/csharp/RoundtripLongToString.cs similarity index 94% rename from snippets/core/system-text-json/csharp/ConvertLongToString.cs rename to snippets/core/system-text-json/csharp/RoundtripLongToString.cs index 6085319e563..20c1c21d41c 100644 --- a/snippets/core/system-text-json/csharp/ConvertLongToString.cs +++ b/snippets/core/system-text-json/csharp/RoundtripLongToString.cs @@ -3,7 +3,7 @@ namespace SystemTextJsonSamples { - public class ConvertLongToString + public class RoundtripLongToString { public static void Run() { diff --git a/snippets/core/system-text-json/csharp/ConvertPolymorphic.cs b/snippets/core/system-text-json/csharp/RoundtripPolymorphic.cs similarity index 96% rename from snippets/core/system-text-json/csharp/ConvertPolymorphic.cs rename to snippets/core/system-text-json/csharp/RoundtripPolymorphic.cs index cb76d4d0460..a771f16132e 100644 --- a/snippets/core/system-text-json/csharp/ConvertPolymorphic.cs +++ b/snippets/core/system-text-json/csharp/RoundtripPolymorphic.cs @@ -5,7 +5,7 @@ namespace SystemTextJsonSamples { - public class ConvertPolymorphic + public class RoundtripPolymorphic { public static void Run() { diff --git a/snippets/core/system-text-json/csharp/WeatherForecastCallbacksConverter.cs b/snippets/core/system-text-json/csharp/WeatherForecastCallbacksConverter.cs index 6cc21c138fb..019f1b5c8a7 100644 --- a/snippets/core/system-text-json/csharp/WeatherForecastCallbacksConverter.cs +++ b/snippets/core/system-text-json/csharp/WeatherForecastCallbacksConverter.cs @@ -11,11 +11,12 @@ public override WeatherForecast Read( Type type, JsonSerializerOptions options) { - // "before" code needs to go into the POCO constructor; code here doesn't have the POCO instance. + // Place "before" code here (OnDeserializing), but note that there is no access here to the POCO instance. + Console.WriteLine("OnDeserializing"); WeatherForecast value = JsonSerializer.Deserialize(ref reader); // note: "options" not passed in - // Place "after" code here (e.g. OnDeserialized) + // Place "after" code here (OnDeserialized) Console.WriteLine("OnDeserialized"); return value; @@ -25,12 +26,12 @@ public override void Write( Utf8JsonWriter writer, WeatherForecast value, JsonSerializerOptions options) { - // Place "before" code here (e.g. OnSerializing) + // Place "before" code here (OnSerializing) Console.WriteLine("OnSerializing"); JsonSerializer.Serialize(writer, value); // note: "options" not passed in - // Place "after" code here (e.g. OnSerialized) + // Place "after" code here (OnSerialized) Console.WriteLine("OnSerialized"); } } diff --git a/snippets/core/system-text-json/csharp/WeatherForecastRequiredPropertyConverter.cs b/snippets/core/system-text-json/csharp/WeatherForecastRequiredPropertyConverter.cs new file mode 100644 index 00000000000..53a2b757dbe --- /dev/null +++ b/snippets/core/system-text-json/csharp/WeatherForecastRequiredPropertyConverter.cs @@ -0,0 +1,31 @@ +using System; +using System.Text.Json; +using System.Text.Json.Serialization; + +namespace SystemTextJsonSamples +{ + public class WeatherForecastRequiredPropertyConverter : JsonConverter + { + public override WeatherForecast Read( + ref Utf8JsonReader reader, + Type type, + JsonSerializerOptions options) + { + WeatherForecast value = JsonSerializer.Deserialize(ref reader); // note: "options" not passed in + + // Check for required fields set by values in JSON + if (value.Date == default) + { + throw new JsonException("Required property not received in the JSON"); + }; + return value; + } + + public override void Write( + Utf8JsonWriter writer, + WeatherForecast value, JsonSerializerOptions options) + { + JsonSerializer.Serialize(writer, value); // note: "options" not passed in + } + } +} From b39df9b7395f2b8f2951420a9ea2e787d498e0c0 Mon Sep 17 00:00:00 2001 From: Tom Dykstra Date: Fri, 6 Dec 2019 15:25:17 -0800 Subject: [PATCH 06/10] draft - attr runtime excl --- .../system-text-json/csharp/ImmutablePoint.cs | 19 +++ .../csharp/ImmutablePointConverter.cs | 113 ++++++++++++++++++ .../OmitOutOfRangeTemperatureConverter.cs | 25 ++++ .../core/system-text-json/csharp/Program.cs | 6 + .../csharp/RoundtripImmutableStruct.cs | 35 ++++++ .../SerializeRuntimePropertyExclusion.cs | 37 ++++++ .../csharp/WeatherForecast.cs | 27 +++++ ... WeatherForecastRuntimeIgnoreConverter.cs} | 39 ++---- 8 files changed, 271 insertions(+), 30 deletions(-) create mode 100644 snippets/core/system-text-json/csharp/ImmutablePoint.cs create mode 100644 snippets/core/system-text-json/csharp/ImmutablePointConverter.cs create mode 100644 snippets/core/system-text-json/csharp/OmitOutOfRangeTemperatureConverter.cs create mode 100644 snippets/core/system-text-json/csharp/RoundtripImmutableStruct.cs create mode 100644 snippets/core/system-text-json/csharp/SerializeRuntimePropertyExclusion.cs rename snippets/core/system-text-json/csharp/{WeatherForecastConverterX.cs => WeatherForecastRuntimeIgnoreConverter.cs} (52%) diff --git a/snippets/core/system-text-json/csharp/ImmutablePoint.cs b/snippets/core/system-text-json/csharp/ImmutablePoint.cs new file mode 100644 index 00000000000..9a47ed7793d --- /dev/null +++ b/snippets/core/system-text-json/csharp/ImmutablePoint.cs @@ -0,0 +1,19 @@ +namespace SystemTextJsonSamples +{ + // + public struct ImmutablePoint + { + private readonly int _x; + private readonly int _y; + + public ImmutablePoint(int x, int y) + { + _x = x; + _y = y; + } + + public int X { get { return _x; } } + public int Y { get { return _y; } } + } + // +} diff --git a/snippets/core/system-text-json/csharp/ImmutablePointConverter.cs b/snippets/core/system-text-json/csharp/ImmutablePointConverter.cs new file mode 100644 index 00000000000..2bccda0567d --- /dev/null +++ b/snippets/core/system-text-json/csharp/ImmutablePointConverter.cs @@ -0,0 +1,113 @@ +using System; +using System.Text.Json; +using System.Text.Json.Serialization; + +namespace SystemTextJsonSamples +{ + public class ImmutablePointConverter : JsonConverter + + { + private const string XName = "X"; + private const string YName = "Y"; + private static readonly JsonEncodedText _keyName = JsonEncodedText.Encode(XName, encoder: null); + private static readonly JsonEncodedText _valueName = JsonEncodedText.Encode(YName, encoder: null); + + public override ImmutablePoint Read( + ref Utf8JsonReader reader, + Type typeToConvert, + JsonSerializerOptions options) + { + if (reader.TokenType != JsonTokenType.StartObject) + { + throw new JsonException(); + }; + + int x = default; + bool xSet = false; + + int y = default; + bool ySet = false; + + // Get the first property. + reader.Read(); + if (reader.TokenType != JsonTokenType.PropertyName) + { + throw new JsonException(); + } + + string propertyName = reader.GetString(); + if (propertyName == XName) + { + x = ReadProperty(ref reader, options); + xSet = true; + } + else if (propertyName == YName) + { + y = ReadProperty(ref reader, options); + ySet = true; + } + else + { + throw new JsonException(); + } + + // Get the second property. + reader.Read(); + if (reader.TokenType != JsonTokenType.PropertyName) + { + throw new JsonException(); + } + + propertyName = reader.GetString(); + if (propertyName == XName) + { + x = ReadProperty(ref reader, options); + xSet = true; + } + else if (propertyName == YName) + { + y = ReadProperty(ref reader, options); + ySet = true; + } + else + { + throw new JsonException(); + } + + if (!xSet || !ySet) + { + throw new JsonException(); + } + + reader.Read(); + + if (reader.TokenType != JsonTokenType.EndObject) + { + throw new JsonException(); + } + + return new ImmutablePoint(x, y); + } + + public int ReadProperty(ref Utf8JsonReader reader, JsonSerializerOptions options) + { + if (options?.GetConverter(typeof(int)) is JsonConverter intConverter) + { + reader.Read(); + return intConverter.Read(ref reader, typeof(int), options); + } + else + { + throw new JsonException(); + } + } + + public override void Write( + Utf8JsonWriter writer, + ImmutablePoint value, + JsonSerializerOptions options) + { + JsonSerializer.Serialize(writer, value); // note: "options" not passed in + } + } +} diff --git a/snippets/core/system-text-json/csharp/OmitOutOfRangeTemperatureConverter.cs b/snippets/core/system-text-json/csharp/OmitOutOfRangeTemperatureConverter.cs new file mode 100644 index 00000000000..58a9b3b2cea --- /dev/null +++ b/snippets/core/system-text-json/csharp/OmitOutOfRangeTemperatureConverter.cs @@ -0,0 +1,25 @@ +using System; +using System.Buffers; +using System.Buffers.Text; +using System.Runtime.InteropServices.WindowsRuntime; +using System.Text.Json; +using System.Text.Json.Serialization; + +namespace SystemTextJsonSamples +{ + public class OmitOutOfRangeTemperatureConverter : JsonConverter + { + public override int Read(ref Utf8JsonReader reader, Type type, JsonSerializerOptions options) + { + return reader.GetInt32(); + } + + public override void Write(Utf8JsonWriter writer, int value, JsonSerializerOptions options) + { + if (value > -90 && value < 90) + { + writer.WriteNumberValue(value); + } + } + } +} diff --git a/snippets/core/system-text-json/csharp/Program.cs b/snippets/core/system-text-json/csharp/Program.cs index 00533ced72a..27d4e739c7f 100644 --- a/snippets/core/system-text-json/csharp/Program.cs +++ b/snippets/core/system-text-json/csharp/Program.cs @@ -91,6 +91,12 @@ static async Task Main(string[] args) Console.WriteLine("\n============================= Null value to nonnullable type\n"); DeserializeNullToNonnullableType.Run(); + Console.WriteLine("\n============================= Immutable struct\n"); + RoundtripImmutableStruct.Run(); + + Console.WriteLine("\n============================= Runtime property exclusion\n"); + SerializeRuntimePropertyExclusion.Run(); + Console.WriteLine("\n============================= JsonDocument data access\n"); JsonDocumentDataAccess.Run(); diff --git a/snippets/core/system-text-json/csharp/RoundtripImmutableStruct.cs b/snippets/core/system-text-json/csharp/RoundtripImmutableStruct.cs new file mode 100644 index 00000000000..e53ea0fa427 --- /dev/null +++ b/snippets/core/system-text-json/csharp/RoundtripImmutableStruct.cs @@ -0,0 +1,35 @@ +using System; +using System.Collections.Generic; +using System.Text.Json; +using System.Text.Json.Serialization; +using System.Xml.Schema; + +namespace SystemTextJsonSamples +{ + public class RoundtripImmutableStruct + { + public static void Run() + { + string jsonString; + var point1 = new ImmutablePoint(1, 2); + var point2 = new ImmutablePoint(3, 4); + var points = new List { point1, point2 }; + + var serializeOptions = new JsonSerializerOptions(); + serializeOptions.WriteIndented = true; + //serializeOptions.Converters.Add(new WeatherForecastRequiredPropertyConverter()); + jsonString = JsonSerializer.Serialize(points, serializeOptions); + Console.WriteLine($"JSON output:\n{jsonString}\n"); + + var deserializeOptions = new JsonSerializerOptions(); + deserializeOptions.Converters.Add(new ImmutablePointConverter()); + points = JsonSerializer.Deserialize>(jsonString, deserializeOptions); + Console.WriteLine("Deserialized object values"); + foreach (ImmutablePoint point in points) + { + Console.WriteLine($"X,Y = {point.X},{point.Y}"); + } + } + } + +} diff --git a/snippets/core/system-text-json/csharp/SerializeRuntimePropertyExclusion.cs b/snippets/core/system-text-json/csharp/SerializeRuntimePropertyExclusion.cs new file mode 100644 index 00000000000..ba28e9c63f0 --- /dev/null +++ b/snippets/core/system-text-json/csharp/SerializeRuntimePropertyExclusion.cs @@ -0,0 +1,37 @@ +using System; +using System.Collections.Generic; +using System.Text.Json; +using System.Text.Json.Serialization; + +namespace SystemTextJsonSamples +{ + public class SerializeRuntimePropertyExclusion + { + public static void Run() + { + string jsonString; + var wf = WeatherForecastFactories.CreateWeatherForecastWithTemperatureAttribute(); + wf.DisplayPropertyValues(); + + var serializeOptions = new JsonSerializerOptions(); + serializeOptions.Converters.Add(new OmitOutOfRangeTemperatureConverter()); + serializeOptions.WriteIndented = true; + jsonString = JsonSerializer.Serialize(wf, serializeOptions); + Console.WriteLine($"JSON output:\n{jsonString}\n"); + + wf.TemperatureCelsius = 91; + wf.DisplayPropertyValues(); + serializeOptions = new JsonSerializerOptions(); + serializeOptions.Converters.Add(new OmitOutOfRangeTemperatureConverter()); + serializeOptions.WriteIndented = true; + jsonString = JsonSerializer.Serialize(wf, serializeOptions); + Console.WriteLine($"JSON output:\n{jsonString}\n"); + + var deserializeOptions = new JsonSerializerOptions(); + deserializeOptions.Converters.Add(new OmitOutOfRangeTemperatureConverter()); + wf = JsonSerializer.Deserialize(jsonString, deserializeOptions); + wf.DisplayPropertyValues(); + } + } + +} diff --git a/snippets/core/system-text-json/csharp/WeatherForecast.cs b/snippets/core/system-text-json/csharp/WeatherForecast.cs index 7b9a6771393..4c6e0a0c292 100644 --- a/snippets/core/system-text-json/csharp/WeatherForecast.cs +++ b/snippets/core/system-text-json/csharp/WeatherForecast.cs @@ -23,6 +23,16 @@ public class WeatherForecastWithLong } // + // + public class WeatherForecastWithTemperatureAttribute + { + public DateTimeOffset Date { get; set; } + [JsonConverter(typeof(OmitOutOfRangeTemperatureConverter))] + public int TemperatureCelsius { get; set; } + public string Summary { get; set; } + } + // + // public class WeatherForecastWithDefault { @@ -187,6 +197,12 @@ public static void DisplayPropertyValues(this WeatherForecastWithLong wf) Console.WriteLine(); } + public static void DisplayPropertyValues(this WeatherForecastWithTemperatureAttribute wf) + { + Utilities.DisplayPropertyValues(wf); + Console.WriteLine(); + } + public static void DisplayPropertyValues(this WeatherForecastWithDefault wf) { Utilities.DisplayPropertyValues(wf); @@ -316,6 +332,17 @@ public static WeatherForecastWithLong CreateWeatherForecastWithLong() return weatherForecast; } + public static WeatherForecastWithTemperatureAttribute CreateWeatherForecastWithTemperatureAttribute() + { + var weatherForecast = new WeatherForecastWithTemperatureAttribute + { + Date = DateTime.Parse("2019-08-01"), + TemperatureCelsius = 25, + Summary = "Hot" + }; + return weatherForecast; + } + public static WeatherForecastWithROProperty CreateWeatherForecastWithROProperty() { var weatherForecast = new WeatherForecastWithROProperty diff --git a/snippets/core/system-text-json/csharp/WeatherForecastConverterX.cs b/snippets/core/system-text-json/csharp/WeatherForecastRuntimeIgnoreConverter.cs similarity index 52% rename from snippets/core/system-text-json/csharp/WeatherForecastConverterX.cs rename to snippets/core/system-text-json/csharp/WeatherForecastRuntimeIgnoreConverter.cs index a4ece6972c6..28f266ec7af 100644 --- a/snippets/core/system-text-json/csharp/WeatherForecastConverterX.cs +++ b/snippets/core/system-text-json/csharp/WeatherForecastRuntimeIgnoreConverter.cs @@ -4,12 +4,13 @@ namespace SystemTextJsonSamples { - public class WeatherForecastConverterX : JsonConverter + public class WeatherForecastRuntimeIgnoreConverter : JsonConverter { - public override WeatherForecast Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) + public override WeatherForecast Read( + ref Utf8JsonReader reader, + Type typeToConvert, + JsonSerializerOptions options) { - // Location for OnDeserializing "callback" code. - Console.WriteLine("OnDeserializing"); if (reader.TokenType != JsonTokenType.StartObject) { throw new JsonException(); @@ -21,13 +22,6 @@ public override WeatherForecast Read(ref Utf8JsonReader reader, Type typeToConve { if (reader.TokenType == JsonTokenType.EndObject) { - // Location for OnDeserialized "callback" code. - Console.WriteLine("OnDeserialized"); - // Check for required fields set by values in JSON - if (wf.Date == default(DateTimeOffset)) - { - throw new JsonException("Required property not received in the JSON"); - }; return wf; } @@ -38,16 +32,8 @@ public override WeatherForecast Read(ref Utf8JsonReader reader, Type typeToConve switch (propertyName) { case "Date": - // Avoid exception on getting null in JSON for a non-null value type. - if (reader.TokenType == JsonTokenType.Null) - { - wf.Date = DateTimeOffset.Now; - } - else - { - DateTimeOffset date = reader.GetDateTimeOffset(); - wf.Date = date; - } + DateTimeOffset date = reader.GetDateTimeOffset(); + wf.Date = date; break; case "TemperatureCelsius": int temperatureCelsius = reader.GetInt32(); @@ -55,11 +41,7 @@ public override WeatherForecast Read(ref Utf8JsonReader reader, Type typeToConve break; case "Summary": string summary = reader.GetString(); - // Ignore properties in JSON based on criteria evaluated at runtime. - if (wf.TemperatureCelsius != 0) - { - wf.Summary = summary; - } + wf.Summary = string.IsNullOrWhiteSpace(summary) ? "N/A" : summary; break; } } @@ -77,15 +59,12 @@ public override void Write(Utf8JsonWriter writer, WeatherForecast wf, JsonSerial writer.WriteString("Date", wf.Date); writer.WriteNumber("TemperatureCelsius", wf.TemperatureCelsius); - if (wf.TemperatureCelsius != 0) + if (!string.IsNullOrWhiteSpace(wf.Summary) && wf.Summary != "N/A") { writer.WriteString("Summary", wf.Summary); } writer.WriteEndObject(); - - // Location for Onserialized "callback" code. - Console.WriteLine("OnSerialized"); } } } From 69059ee5b6553c0a239f50ee1d9ee6f7e63b83bc Mon Sep 17 00:00:00 2001 From: Tom Dykstra Date: Fri, 6 Dec 2019 15:39:26 -0800 Subject: [PATCH 07/10] draft --- .../OmitOutOfRangeTemperatureConverter.cs | 25 ----------------- .../SerializeRuntimePropertyExclusion.cs | 10 +++---- .../csharp/WeatherForecast.cs | 27 ------------------- 3 files changed, 5 insertions(+), 57 deletions(-) delete mode 100644 snippets/core/system-text-json/csharp/OmitOutOfRangeTemperatureConverter.cs diff --git a/snippets/core/system-text-json/csharp/OmitOutOfRangeTemperatureConverter.cs b/snippets/core/system-text-json/csharp/OmitOutOfRangeTemperatureConverter.cs deleted file mode 100644 index 58a9b3b2cea..00000000000 --- a/snippets/core/system-text-json/csharp/OmitOutOfRangeTemperatureConverter.cs +++ /dev/null @@ -1,25 +0,0 @@ -using System; -using System.Buffers; -using System.Buffers.Text; -using System.Runtime.InteropServices.WindowsRuntime; -using System.Text.Json; -using System.Text.Json.Serialization; - -namespace SystemTextJsonSamples -{ - public class OmitOutOfRangeTemperatureConverter : JsonConverter - { - public override int Read(ref Utf8JsonReader reader, Type type, JsonSerializerOptions options) - { - return reader.GetInt32(); - } - - public override void Write(Utf8JsonWriter writer, int value, JsonSerializerOptions options) - { - if (value > -90 && value < 90) - { - writer.WriteNumberValue(value); - } - } - } -} diff --git a/snippets/core/system-text-json/csharp/SerializeRuntimePropertyExclusion.cs b/snippets/core/system-text-json/csharp/SerializeRuntimePropertyExclusion.cs index ba28e9c63f0..60d7b101b69 100644 --- a/snippets/core/system-text-json/csharp/SerializeRuntimePropertyExclusion.cs +++ b/snippets/core/system-text-json/csharp/SerializeRuntimePropertyExclusion.cs @@ -10,11 +10,11 @@ public class SerializeRuntimePropertyExclusion public static void Run() { string jsonString; - var wf = WeatherForecastFactories.CreateWeatherForecastWithTemperatureAttribute(); + var wf = WeatherForecastFactories.CreateWeatherForecast(); wf.DisplayPropertyValues(); var serializeOptions = new JsonSerializerOptions(); - serializeOptions.Converters.Add(new OmitOutOfRangeTemperatureConverter()); + serializeOptions.Converters.Add(new WeatherForecastRuntimeIgnoreConverter()); serializeOptions.WriteIndented = true; jsonString = JsonSerializer.Serialize(wf, serializeOptions); Console.WriteLine($"JSON output:\n{jsonString}\n"); @@ -22,14 +22,14 @@ public static void Run() wf.TemperatureCelsius = 91; wf.DisplayPropertyValues(); serializeOptions = new JsonSerializerOptions(); - serializeOptions.Converters.Add(new OmitOutOfRangeTemperatureConverter()); + serializeOptions.Converters.Add(new WeatherForecastRuntimeIgnoreConverter()); serializeOptions.WriteIndented = true; jsonString = JsonSerializer.Serialize(wf, serializeOptions); Console.WriteLine($"JSON output:\n{jsonString}\n"); var deserializeOptions = new JsonSerializerOptions(); - deserializeOptions.Converters.Add(new OmitOutOfRangeTemperatureConverter()); - wf = JsonSerializer.Deserialize(jsonString, deserializeOptions); + deserializeOptions.Converters.Add(new WeatherForecastRuntimeIgnoreConverter()); + wf = JsonSerializer.Deserialize(jsonString, deserializeOptions); wf.DisplayPropertyValues(); } } diff --git a/snippets/core/system-text-json/csharp/WeatherForecast.cs b/snippets/core/system-text-json/csharp/WeatherForecast.cs index 4c6e0a0c292..7b9a6771393 100644 --- a/snippets/core/system-text-json/csharp/WeatherForecast.cs +++ b/snippets/core/system-text-json/csharp/WeatherForecast.cs @@ -23,16 +23,6 @@ public class WeatherForecastWithLong } // - // - public class WeatherForecastWithTemperatureAttribute - { - public DateTimeOffset Date { get; set; } - [JsonConverter(typeof(OmitOutOfRangeTemperatureConverter))] - public int TemperatureCelsius { get; set; } - public string Summary { get; set; } - } - // - // public class WeatherForecastWithDefault { @@ -197,12 +187,6 @@ public static void DisplayPropertyValues(this WeatherForecastWithLong wf) Console.WriteLine(); } - public static void DisplayPropertyValues(this WeatherForecastWithTemperatureAttribute wf) - { - Utilities.DisplayPropertyValues(wf); - Console.WriteLine(); - } - public static void DisplayPropertyValues(this WeatherForecastWithDefault wf) { Utilities.DisplayPropertyValues(wf); @@ -332,17 +316,6 @@ public static WeatherForecastWithLong CreateWeatherForecastWithLong() return weatherForecast; } - public static WeatherForecastWithTemperatureAttribute CreateWeatherForecastWithTemperatureAttribute() - { - var weatherForecast = new WeatherForecastWithTemperatureAttribute - { - Date = DateTime.Parse("2019-08-01"), - TemperatureCelsius = 25, - Summary = "Hot" - }; - return weatherForecast; - } - public static WeatherForecastWithROProperty CreateWeatherForecastWithROProperty() { var weatherForecast = new WeatherForecastWithROProperty From 37f1fc5470d9f148f14f9206bdedc2ac97c92fd9 Mon Sep 17 00:00:00 2001 From: Tom Dykstra Date: Fri, 6 Dec 2019 15:43:51 -0800 Subject: [PATCH 08/10] draft --- .../csharp/SerializeRuntimePropertyExclusion.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/snippets/core/system-text-json/csharp/SerializeRuntimePropertyExclusion.cs b/snippets/core/system-text-json/csharp/SerializeRuntimePropertyExclusion.cs index 60d7b101b69..e56a036aa88 100644 --- a/snippets/core/system-text-json/csharp/SerializeRuntimePropertyExclusion.cs +++ b/snippets/core/system-text-json/csharp/SerializeRuntimePropertyExclusion.cs @@ -19,7 +19,7 @@ public static void Run() jsonString = JsonSerializer.Serialize(wf, serializeOptions); Console.WriteLine($"JSON output:\n{jsonString}\n"); - wf.TemperatureCelsius = 91; + wf.Summary = "N/A"; wf.DisplayPropertyValues(); serializeOptions = new JsonSerializerOptions(); serializeOptions.Converters.Add(new WeatherForecastRuntimeIgnoreConverter()); From 6e14136413a778d5e2eb65842df1e70b9ba1f76d Mon Sep 17 00:00:00 2001 From: Tom Dykstra Date: Wed, 11 Dec 2019 13:16:53 -0800 Subject: [PATCH 09/10] remove comments --- snippets/core/system-text-json/csharp/Utf8ReaderFromFile.cs | 5 ----- .../csharp/WeatherForecastCallbacksConverter.cs | 6 ++++-- .../csharp/WeatherForecastRequiredPropertyConverter.cs | 6 ++++-- 3 files changed, 8 insertions(+), 9 deletions(-) diff --git a/snippets/core/system-text-json/csharp/Utf8ReaderFromFile.cs b/snippets/core/system-text-json/csharp/Utf8ReaderFromFile.cs index b5855f66993..1eea31b8757 100644 --- a/snippets/core/system-text-json/csharp/Utf8ReaderFromFile.cs +++ b/snippets/core/system-text-json/csharp/Utf8ReaderFromFile.cs @@ -11,16 +11,11 @@ class Utf8ReaderFromFile public static void Run() { // Read as UTF-16 and transcode to UTF-8 to convert to a Span - //***The following code also works with the Utf8 file. - string fileName = "Universities.json"; string jsonString = File.ReadAllText(fileName); ReadOnlySpan jsonReadOnlySpan = Encoding.UTF8.GetBytes(jsonString); // Or ReadAllBytes if the file encoding is UTF-8: - //***But this code doesn't work with the UTF-16 file - //System.Text.Json.JsonReaderException: '0xEF' is an invalid start of a value. LineNumber: 0 | BytePositionInLine: 0. - //string fileName = "UniversitiesUtf8.json"; //ReadOnlySpan jsonReadOnlySpan = File.ReadAllBytes(fileName); diff --git a/snippets/core/system-text-json/csharp/WeatherForecastCallbacksConverter.cs b/snippets/core/system-text-json/csharp/WeatherForecastCallbacksConverter.cs index 019f1b5c8a7..c5b364eab82 100644 --- a/snippets/core/system-text-json/csharp/WeatherForecastCallbacksConverter.cs +++ b/snippets/core/system-text-json/csharp/WeatherForecastCallbacksConverter.cs @@ -14,7 +14,8 @@ public override WeatherForecast Read( // Place "before" code here (OnDeserializing), but note that there is no access here to the POCO instance. Console.WriteLine("OnDeserializing"); - WeatherForecast value = JsonSerializer.Deserialize(ref reader); // note: "options" not passed in + // Don't pass in options when recursively calling Deserialize. + WeatherForecast value = JsonSerializer.Deserialize(ref reader); // Place "after" code here (OnDeserialized) Console.WriteLine("OnDeserialized"); @@ -29,7 +30,8 @@ public override void Write( // Place "before" code here (OnSerializing) Console.WriteLine("OnSerializing"); - JsonSerializer.Serialize(writer, value); // note: "options" not passed in + // Don't pass in options when recursively calling Serialize. + JsonSerializer.Serialize(writer, value); // Place "after" code here (OnSerialized) Console.WriteLine("OnSerialized"); diff --git a/snippets/core/system-text-json/csharp/WeatherForecastRequiredPropertyConverter.cs b/snippets/core/system-text-json/csharp/WeatherForecastRequiredPropertyConverter.cs index 53a2b757dbe..ed7e8ec2225 100644 --- a/snippets/core/system-text-json/csharp/WeatherForecastRequiredPropertyConverter.cs +++ b/snippets/core/system-text-json/csharp/WeatherForecastRequiredPropertyConverter.cs @@ -11,7 +11,8 @@ public override WeatherForecast Read( Type type, JsonSerializerOptions options) { - WeatherForecast value = JsonSerializer.Deserialize(ref reader); // note: "options" not passed in + // Don't pass in options when recursively calling Deserialize. + WeatherForecast value = JsonSerializer.Deserialize(ref reader); // Check for required fields set by values in JSON if (value.Date == default) @@ -25,7 +26,8 @@ public override void Write( Utf8JsonWriter writer, WeatherForecast value, JsonSerializerOptions options) { - JsonSerializer.Serialize(writer, value); // note: "options" not passed in + // Don't pass in options when recursively calling Serialize. + JsonSerializer.Serialize(writer, value); } } } From b1c945b488b5966f816e92ddac7c002f9656b635 Mon Sep 17 00:00:00 2001 From: Tom Dykstra Date: Wed, 11 Dec 2019 14:45:13 -0800 Subject: [PATCH 10/10] fix bug --- .../core/system-text-json/csharp/ImmutablePointConverter.cs | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/snippets/core/system-text-json/csharp/ImmutablePointConverter.cs b/snippets/core/system-text-json/csharp/ImmutablePointConverter.cs index 2bccda0567d..a45d657e790 100644 --- a/snippets/core/system-text-json/csharp/ImmutablePointConverter.cs +++ b/snippets/core/system-text-json/csharp/ImmutablePointConverter.cs @@ -9,8 +9,6 @@ public class ImmutablePointConverter : JsonConverter { private const string XName = "X"; private const string YName = "Y"; - private static readonly JsonEncodedText _keyName = JsonEncodedText.Encode(XName, encoder: null); - private static readonly JsonEncodedText _valueName = JsonEncodedText.Encode(YName, encoder: null); public override ImmutablePoint Read( ref Utf8JsonReader reader, @@ -107,7 +105,8 @@ public override void Write( ImmutablePoint value, JsonSerializerOptions options) { - JsonSerializer.Serialize(writer, value); // note: "options" not passed in + // Don't pass in options when recursively calling Serialize. + JsonSerializer.Serialize(writer, value); } } }