From bd2ae47c97e61f7f132ddbc6f260d55d6872bcc5 Mon Sep 17 00:00:00 2001 From: Vladimir Pecanac Date: Sun, 25 Oct 2026 13:27:35 +0100 Subject: [PATCH] Port JsonSerializeDictionary to .NET 10 and add a dictionary key converter Retarget all three projects to net10.0, bump Newtonsoft.Json to 13.0.4 and the test packages, add CustomerKeyConverter (WriteAsPropertyName) with two tests, and add the key converter, Guid key projection and DictionaryKeyPolicy examples to Program.cs. --- .../JsonSerializeToDictionary.csproj | 4 +-- .../src/JsonSerializeToDictionary/Program.cs | 34 +++++++++++++++++++ .../CustomerKeyConverter.cs | 21 ++++++++++++ .../JsonSerializeToDictionaryDataModel.csproj | 4 +-- .../CustomerKeyConverterUnitTest.cs | 31 +++++++++++++++++ ...erializeToDictionaryDataModel.Tests.csproj | 14 ++++---- 6 files changed, 97 insertions(+), 11 deletions(-) create mode 100644 json-csharp/JsonSerializeDictionary/src/JsonSerializeToDictionaryDataModel/CustomerKeyConverter.cs create mode 100644 json-csharp/JsonSerializeDictionary/test/JsonSerializeToDictionaryDataModel.Tests/CustomerKeyConverterUnitTest.cs diff --git a/json-csharp/JsonSerializeDictionary/src/JsonSerializeToDictionary/JsonSerializeToDictionary.csproj b/json-csharp/JsonSerializeDictionary/src/JsonSerializeToDictionary/JsonSerializeToDictionary.csproj index ad9f144d84..a4f71f9f34 100644 --- a/json-csharp/JsonSerializeDictionary/src/JsonSerializeToDictionary/JsonSerializeToDictionary.csproj +++ b/json-csharp/JsonSerializeDictionary/src/JsonSerializeToDictionary/JsonSerializeToDictionary.csproj @@ -2,13 +2,13 @@ Exe - net7.0 + net10.0 enable enable - + diff --git a/json-csharp/JsonSerializeDictionary/src/JsonSerializeToDictionary/Program.cs b/json-csharp/JsonSerializeDictionary/src/JsonSerializeToDictionary/Program.cs index 1bebe7d344..306fc7796f 100644 --- a/json-csharp/JsonSerializeDictionary/src/JsonSerializeToDictionary/Program.cs +++ b/json-csharp/JsonSerializeDictionary/src/JsonSerializeToDictionary/Program.cs @@ -64,4 +64,38 @@ WriteIndented = true }); Console.WriteLine(systemJsonComplex); +Console.WriteLine(); + +// Using System.Text.Json with a converter for the key type only +Console.WriteLine("============ Complex Dictionary: System.Text.Json w/ Key Converter ============"); +var systemJsonKeyConverter = JsonSerializer.Serialize(invoicesByCustomer, new JsonSerializerOptions +{ + Converters = { new CustomerKeyConverter() }, + WriteIndented = true +}); +Console.WriteLine(systemJsonKeyConverter); +Console.WriteLine(); + +// Using System.Text.Json after projecting the keys to Guid +Console.WriteLine("============ Complex Dictionary: System.Text.Json w/ Guid Keys ============"); +var invoicesByCustomerId = invoicesByCustomer.ToDictionary(pair => pair.Key.CustomerId, pair => pair.Value); +var systemJsonGuidKeys = JsonSerializer.Serialize(invoicesByCustomerId, new JsonSerializerOptions +{ + WriteIndented = true +}); +Console.WriteLine(systemJsonGuidKeys); +Console.WriteLine(); + +// Using System.Text.Json with a naming policy for the keys +Console.WriteLine("============ System.Text.Json - camelCase Keys ============"); +var stockByProduct = new Dictionary +{ + {"AppleCount", 3}, + {"OrangeCount", 5} +}; +var systemJsonCamelCaseKeys = JsonSerializer.Serialize(stockByProduct, new JsonSerializerOptions +{ + DictionaryKeyPolicy = JsonNamingPolicy.CamelCase +}); +Console.WriteLine(systemJsonCamelCaseKeys); Console.WriteLine(); \ No newline at end of file diff --git a/json-csharp/JsonSerializeDictionary/src/JsonSerializeToDictionaryDataModel/CustomerKeyConverter.cs b/json-csharp/JsonSerializeDictionary/src/JsonSerializeToDictionaryDataModel/CustomerKeyConverter.cs new file mode 100644 index 0000000000..5ecf6448c9 --- /dev/null +++ b/json-csharp/JsonSerializeDictionary/src/JsonSerializeToDictionaryDataModel/CustomerKeyConverter.cs @@ -0,0 +1,21 @@ +using System.Text.Json; +using System.Text.Json.Serialization; + +namespace JsonSerializeToDictionaryDataModel; + +public sealed class CustomerKeyConverter : JsonConverter +{ + public override Customer Read(ref Utf8JsonReader reader, Type typeToConvert, + JsonSerializerOptions options) + { + // Since we are only dealing with Serializing in this article, we don't need to implement the Read method + throw new NotImplementedException(); + } + + public override void Write(Utf8JsonWriter writer, Customer value, JsonSerializerOptions options) + => writer.WriteStringValue($"Customer-{value.CustomerId:N}"); + + public override void WriteAsPropertyName(Utf8JsonWriter writer, Customer value, + JsonSerializerOptions options) + => writer.WritePropertyName($"Customer-{value.CustomerId:N}"); +} diff --git a/json-csharp/JsonSerializeDictionary/src/JsonSerializeToDictionaryDataModel/JsonSerializeToDictionaryDataModel.csproj b/json-csharp/JsonSerializeDictionary/src/JsonSerializeToDictionaryDataModel/JsonSerializeToDictionaryDataModel.csproj index 69954a37c1..dde396945a 100644 --- a/json-csharp/JsonSerializeDictionary/src/JsonSerializeToDictionaryDataModel/JsonSerializeToDictionaryDataModel.csproj +++ b/json-csharp/JsonSerializeDictionary/src/JsonSerializeToDictionaryDataModel/JsonSerializeToDictionaryDataModel.csproj @@ -1,13 +1,13 @@ - net7.0 + net10.0 enable enable - + \ No newline at end of file diff --git a/json-csharp/JsonSerializeDictionary/test/JsonSerializeToDictionaryDataModel.Tests/CustomerKeyConverterUnitTest.cs b/json-csharp/JsonSerializeDictionary/test/JsonSerializeToDictionaryDataModel.Tests/CustomerKeyConverterUnitTest.cs new file mode 100644 index 0000000000..9c04941787 --- /dev/null +++ b/json-csharp/JsonSerializeDictionary/test/JsonSerializeToDictionaryDataModel.Tests/CustomerKeyConverterUnitTest.cs @@ -0,0 +1,31 @@ +using System.Text.Json; + +namespace JsonSerializeToDictionaryDataModel.Tests; + +public class CustomerKeyConverterUnitTest +{ + private static readonly Customer Customer = new( + "123 Nowhere Street, Fakeville, MD", + "555-555-0001", + new Person("John", "Smith"), + Guid.Parse("bfafbb48-f670-4e0e-87f6-d44ac219cf7b")); + + [Fact] + public void GivenDictionaryWithCustomerKeys_WhenSerializedWithoutConverter_ThenThrowsNotSupportedException() + { + var dictionary = new Dictionary { { Customer, 2 } }; + + Assert.Throws(() => JsonSerializer.Serialize(dictionary)); + } + + [Fact] + public void GivenDictionaryWithCustomerKeys_WhenSerializedWithKeyConverter_ThenKeyIsCustomerId() + { + var dictionary = new Dictionary { { Customer, 2 } }; + var options = new JsonSerializerOptions { Converters = { new CustomerKeyConverter() } }; + + var result = JsonSerializer.Serialize(dictionary, options); + + Assert.Equal("{\"Customer-bfafbb48f6704e0e87f6d44ac219cf7b\":2}", result); + } +} diff --git a/json-csharp/JsonSerializeDictionary/test/JsonSerializeToDictionaryDataModel.Tests/JsonSerializeToDictionaryDataModel.Tests.csproj b/json-csharp/JsonSerializeDictionary/test/JsonSerializeToDictionaryDataModel.Tests/JsonSerializeToDictionaryDataModel.Tests.csproj index f195456205..72bf56e2bc 100644 --- a/json-csharp/JsonSerializeDictionary/test/JsonSerializeToDictionaryDataModel.Tests/JsonSerializeToDictionaryDataModel.Tests.csproj +++ b/json-csharp/JsonSerializeDictionary/test/JsonSerializeToDictionaryDataModel.Tests/JsonSerializeToDictionaryDataModel.Tests.csproj @@ -1,7 +1,7 @@  - net7.0 + net10.0 enable enable @@ -10,15 +10,15 @@ - - - - - + + + + + runtime; build; native; contentfiles; analyzers; buildtransitive all - + runtime; build; native; contentfiles; analyzers; buildtransitive all