[Fork] [Fork] [Fork] How can I use default serialization inside a System.Text.Json JsonConverter?

CSharp · Console

/Program.cs

//https://stackoverflow.com/questions/65430420/how-to-use-default-serialization-in-a-custom-system-text-json-jsonconverter
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Text;

using System.Text.Json;
using System.Text.Json.Serialization;

using NUnit.Framework; // Throws NUnit.Framework.AssertionException

//How can I use default serialization inside a System.Text.Json JsonConverter?

	//	public record Person(string Name);

	public record Person(string FirstName, string LastName);

	public sealed class PersonConverter : DefaultConverterFactory<Person>
	{
		record PersonDTO(string FirstName, string LastName, string Name); // A DTO with both the old and new properties.

		protected override Person Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions modifiedOptions)
		{
			var dto = JsonSerializer.Deserialize<PersonDTO>(ref reader, modifiedOptions);
			var oldNames = dto?.Name?.Split(' ', StringSplitOptions.RemoveEmptyEntries) ?? Enumerable.Empty<string>();
			return new Person(dto.FirstName ?? oldNames.FirstOrDefault(), dto.LastName ?? oldNames.LastOrDefault());
		}
	}

	public abstract class DefaultConverterFactory<T> : JsonConverterFactory
	{
		class DefaultConverter : JsonConverter<T>
		{
			readonly JsonSerializerOptions modifiedOptions;
			readonly DefaultConverterFactory<T> factory;

			public DefaultConverter(JsonSerializerOptions options, DefaultConverterFactory<T> factory)
			{
				this.factory = factory;
				this.modifiedOptions = options.CopyAndRemoveConverter(factory.GetType());
			}

			public override void Write(Utf8JsonWriter writer, T value, JsonSerializerOptions options) => factory.Write(writer, value, modifiedOptions);

			public override T Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) => factory.Read(ref reader, typeToConvert, modifiedOptions);
		}

		protected virtual T Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions modifiedOptions)
			=> (T)JsonSerializer.Deserialize(ref reader, typeToConvert, modifiedOptions);

		protected virtual void Write(Utf8JsonWriter writer, T value, JsonSerializerOptions modifiedOptions) 
			=> JsonSerializer.Serialize(writer, value, modifiedOptions);

		public override bool CanConvert(Type typeToConvert) => typeof(T) == typeToConvert;

		public override JsonConverter CreateConverter(Type typeToConvert, JsonSerializerOptions options) => new DefaultConverter(options, this);
	}

	public static class JsonSerializerExtensions
	{
		public static JsonSerializerOptions CopyAndRemoveConverter(this JsonSerializerOptions options, Type converterType)
		{
			var copy = new JsonSerializerOptions(options);
			for (var i = copy.Converters.Count - 1; i >= 0; i--)
				if (copy.Converters[i].GetType() == converterType)
					copy.Converters.RemoveAt(i);
			return copy;
		}
	}

    class TestClass
    {
        public static void Test()
        {
			foreach (var json in GetJson())
			{
				var options = new JsonSerializerOptions
				{
					PropertyNamingPolicy = JsonNamingPolicy.CamelCase,
					Converters = { new PersonConverter() },
				};
				var person = JsonSerializer.Deserialize<List<Person>>(json, options);
				var json2 = JsonSerializer.Serialize(person, options);

				Console.WriteLine(json);
				person.ForEach(p => Console.WriteLine("  " + p));
				Console.WriteLine(json2);
				Console.WriteLine();
				
				var person2 = JsonSerializer.Deserialize<List<Person>>(json2, options);
				Assert.That(person.SequenceEqual(person2));
			}
		}	
		
		static IEnumerable<string> GetJson() =>
			new []
			{
				@"[{""lastName"":""LastName"",""firstName"":""FirstName""}]",
				@"[{""name"":""Full Name""}]",
				@"[{""name"":""Full Name_1""},{""name"":""Full Name_2""},{""lastName"":""LastName_3"",""firstName"":""First Name""}]",
			};
	}

	public class Program
	{
		public static void Main()
		{
			Console.WriteLine("Environment version: {0} ({1}), {2}", System.Runtime.InteropServices.RuntimeInformation.FrameworkDescription, Environment.Version, Environment.OSVersion);
			Console.WriteLine("System.Text.Json version: " + typeof(JsonSerializer).Assembly.FullName);
			Console.WriteLine();

			try
			{
				TestClass.Test();
			}
			catch (Exception ex)
			{
				Console.WriteLine("Failed with unhandled exception: ");
				Console.WriteLine(ex);
				throw;
			}
		}
	}
;