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 interface IAmount {
[Newtonsoft.Json.JsonConverter(typeof(NewtonsoftJsonConverter))]
[System.Text.Json.Serialization.JsonConverter(typeof(SystemTextJsonConverter))]
public class Amount : IAmount {
public Amount(decimal value) {
public decimal Value { get; }
public class NewtonsoftJsonConverter : Newtonsoft.Json.JsonConverter {
public override bool CanConvert(Type objectType) => objectType.IsAssignableTo(typeof(IAmount));
public override object? ReadJson(Newtonsoft.Json.JsonReader reader, Type objectType, object? existingValue, Newtonsoft.Json.JsonSerializer serializer) {
throw new NotImplementedException();
public override void WriteJson(Newtonsoft.Json.JsonWriter writer, object? value, Newtonsoft.Json.JsonSerializer serializer) {
writer.WriteRawValue(((IAmount?)value)?.Value.ToString());
public class SystemTextJsonConverter : System.Text.Json.Serialization.JsonConverter<object> {
public override bool CanConvert(Type typeToConvert) => typeToConvert.IsAssignableTo(typeof(IAmount));
public override object Read(ref System.Text.Json.Utf8JsonReader reader, Type typeToConvert, System.Text.Json.JsonSerializerOptions options) {
throw new NotImplementedException();
public override void Write(System.Text.Json.Utf8JsonWriter writer, object value, System.Text.Json.JsonSerializerOptions options) {
writer.WriteRawValue(((IAmount)value).Value.ToString());
public class ConcreteInterfaceSerializer : JsonConverterFactory
public override bool CanConvert(Type typeToConvert) => typeToConvert.IsInterface;
class ConcreteInterfaceSerializerOfType<TInterface> : JsonConverter<TInterface>
static ConcreteInterfaceSerializerOfType()
if (!typeof(TInterface).IsAbstract && !typeof(TInterface).IsInterface)
throw new NotImplementedException(string.Format("Concrete class {0} is not supported", typeof(TInterface)));
public override TInterface? Read(ref System.Text.Json.Utf8JsonReader reader, Type typeToConvert, System.Text.Json.JsonSerializerOptions options) =>
throw new NotImplementedException();
public override void Write(System.Text.Json.Utf8JsonWriter writer, TInterface value, System.Text.Json.JsonSerializerOptions options) =>
JsonSerializer.Serialize<object>(writer, value!, options);
public override JsonConverter CreateConverter(Type type, JsonSerializerOptions options) =>
(JsonConverter)Activator.CreateInstance(
typeof(ConcreteInterfaceSerializerOfType<>).MakeGenericType(new Type[] { type }),
BindingFlags.Instance | BindingFlags.Public,
args: Array.Empty<object>(),
culture: null).ThrowOnNull();
public static class ObjectExtensions
public static T ThrowOnNull<T>(this T? value) where T : class => value ?? throw new ArgumentNullException();
public static void Test()
var foo = new Amount(10);
var newtonJson = Newtonsoft.Json.JsonConvert.SerializeObject(foo);
var options = new JsonSerializerOptions
Converters = { new ConcreteInterfaceSerializer() },
var systemJson = System.Text.Json.JsonSerializer.Serialize<IAmount>(foo, options);
Console.WriteLine("Newtonsoft JSON:\n {0}", newtonJson);
Console.WriteLine("System.Text.Json JSON:\n {0}", systemJson);
Assert.AreEqual(newtonJson, systemJson);
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("Failed with unhandled exception: ");