using System.Collections;
using System.Collections.Generic;
using System.Runtime.Serialization.Formatters;
using System.ComponentModel.DataAnnotations;
using System.Globalization;
using System.Collections.ObjectModel;
using System.Text.Json.Serialization;
public class EventDefinition
public int EventDefinitionId { get; set; }
public class EntityConverter : DefaultConverterFactory<EventDefinition>
protected override bool HandleNull => true;
protected override EventDefinition Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions modifiedOptions, JsonConverter<EventDefinition> defaultConverter)
var result = base.Read(ref reader, typeToConvert, modifiedOptions, defaultConverter);
return result?.EventDefinitionId == 0 ? null : result;
protected override void Write(Utf8JsonWriter writer, EventDefinition value, JsonSerializerOptions modifiedOptions, JsonConverter<EventDefinition> defaultConverter)
value ??= new EventDefinition { EventDefinitionId = 0 };
base.Write(writer, value, modifiedOptions, defaultConverter);
public abstract class DefaultConverterFactory<T> : JsonConverterFactory
class NullHandlingDefaultConverter : DefaultConverter
public NullHandlingDefaultConverter(JsonSerializerOptions options, DefaultConverterFactory<T> factory) : base(options, factory) { }
public override bool HandleNull => true;
class DefaultConverter : JsonConverter<T>
readonly JsonSerializerOptions modifiedOptions;
readonly DefaultConverterFactory<T> factory;
readonly JsonConverter<T> defaultConverter;
public DefaultConverter(JsonSerializerOptions options, DefaultConverterFactory<T> factory)
this.factory = factory ?? throw new ArgumentNullException();
this.modifiedOptions = options.CopyAndRemoveConverter(factory.GetType());
this.defaultConverter = (JsonConverter<T>)modifiedOptions.GetConverter(typeof(T));
public override void Write(Utf8JsonWriter writer, T value, JsonSerializerOptions options) => factory.Write(writer, value, modifiedOptions, defaultConverter);
public override T Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) => factory.Read(ref reader, typeToConvert, modifiedOptions, defaultConverter);
protected virtual T Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions modifiedOptions, JsonConverter<T> defaultConverter)
=> defaultConverter.ReadOrSerialize<T>(ref reader, typeToConvert, modifiedOptions);
protected virtual void Write(Utf8JsonWriter writer, T value, JsonSerializerOptions modifiedOptions, JsonConverter<T> defaultConverter)
=> defaultConverter.WriteOrSerialize(writer, value, modifiedOptions);
protected virtual bool HandleNull => false;
public override bool CanConvert(Type typeToConvert) => typeof(T) == typeToConvert;
public override JsonConverter CreateConverter(Type typeToConvert, JsonSerializerOptions options) => HandleNull ? new NullHandlingDefaultConverter(options, this) : 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);
public static void WriteOrSerialize<T>(this JsonConverter<T> converter, Utf8JsonWriter writer, T value, JsonSerializerOptions options)
converter.Write(writer, value, options);
JsonSerializer.Serialize(writer, value, options);
public static T ReadOrSerialize<T>(this JsonConverter<T> converter, ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
return converter.Read(ref reader, typeToConvert, options);
return (T)JsonSerializer.Deserialize(ref reader, typeToConvert, options);
public static void Test()
var events = new List<EventDefinition>
new () { EventDefinitionId = 101 },
new () { EventDefinitionId = 202 }
var options = new JsonSerializerOptions
Converters = { new EntityConverter() },
var json = JsonSerializer.Serialize(events, options);
Console.WriteLine("Serializing the {0} list with {1}:", nameof(EventDefinition), nameof(EntityConverter));
var events2 = JsonSerializer.Deserialize<List<EventDefinition>>(json, options);
Assert.IsTrue(events.Select(e => e?.EventDefinitionId).SequenceEqual(events2.Select(e => e?.EventDefinitionId)));
Console.WriteLine("Serializing the {0} list without {1}:", nameof(EventDefinition), nameof(EntityConverter));
var json2 = JsonSerializer.Serialize(events2, new JsonSerializerOptions { WriteIndented = true });
Console.WriteLine(json2);
public static void Main()
Console.WriteLine("Environment version: {0} ({1})", System.Runtime.InteropServices.RuntimeInformation.FrameworkDescription , GetNetCoreVersion());
Console.WriteLine("System.Text.Json version: " + typeof(JsonSerializer).Assembly.FullName);
Console.WriteLine("Failed with unhandled exception: ");
public static string GetNetCoreVersion()
var assembly = typeof(System.Runtime.GCSettings).GetTypeInfo().Assembly;
var assemblyPath = assembly.Location.Split(new[] { '/', '\\' }, StringSplitOptions.RemoveEmptyEntries);
int netCoreAppIndex = Array.IndexOf(assemblyPath, "Microsoft.NETCore.App");
if (netCoreAppIndex > 0 && netCoreAppIndex < assemblyPath.Length - 2)
return assemblyPath[netCoreAppIndex + 1];