using System.Collections;
using System.Collections.Generic;
using Newtonsoft.Json.Linq;
using Newtonsoft.Json.Converters;
using Newtonsoft.Json.Serialization;
public class ListItemConverterDecorator : JsonConverter
readonly JsonConverter itemConverter;
public ListItemConverterDecorator(Type type) =>
itemConverter = (JsonConverter)Activator.CreateInstance(type ?? throw new ArgumentNullException());
public override bool CanConvert(Type objectType) =>
!objectType.IsPrimitive && objectType != typeof(string) && objectType.BaseTypesAndSelf().Any(t => t.IsGenericType && t.GetGenericTypeDefinition() == typeof(List<>));
public override bool CanRead => itemConverter.CanRead;
public override bool CanWrite => itemConverter.CanWrite;
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
var itemType = objectType.BaseTypesAndSelf().Where(t => t.IsGenericType && t.GetGenericTypeDefinition() == typeof(List<>)).Select(t => t.GetGenericArguments()[0]).First();
if (reader.MoveToContentAndAssert().TokenType == JsonToken.Null)
if (reader.TokenType != JsonToken.StartArray)
throw new JsonSerializationException(string.Format("Unexpected token {0}, expected {1}", reader.TokenType, JsonToken.StartArray));
var list = existingValue as IList ?? (IList)serializer.ContractResolver.ResolveContract(objectType).DefaultCreator();
while (reader.ReadToContentAndAssert().TokenType != JsonToken.EndArray)
list.Add(itemConverter.ReadJson(reader, itemType, null, serializer));
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
writer.WriteStartArray();
foreach (var item in (IList)value)
itemConverter.WriteJson(writer, item, serializer);
public static partial class JsonExtensions
public static JsonReader ReadToContentAndAssert(this JsonReader reader) =>
reader.ReadAndAssert().MoveToContentAndAssert();
public static JsonReader MoveToContentAndAssert(this JsonReader reader)
throw new ArgumentNullException();
if (reader.TokenType == JsonToken.None)
while (reader.TokenType == JsonToken.Comment)
public static JsonReader ReadAndAssert(this JsonReader reader)
throw new ArgumentNullException();
throw new JsonReaderException("Unexpected end of JSON stream.");
public static class TypeExtensions
public static IEnumerable<Type> BaseTypesAndSelf(this Type type)
[JsonConverter(typeof(CustomConverter))]
public int ExampleInt { get; set; }
[JsonProperty(ItemConverterType = typeof(CustomConverter))]
public IntList ExampleList { get; set; }
[JsonProperty(ItemConverterType = typeof(ListItemConverterDecorator),
ItemConverterParameters = new object [] { typeof(CustomConverter) })]
public Dictionary<string, List<int>> ExampleDictionary { get; set; }
public class IntList : List<int> { }
public class CustomConverter : JsonConverter<int>
public override void WriteJson(JsonWriter writer, int value, JsonSerializer serializer) =>
writer.WriteValue(-value);
public override int ReadJson(JsonReader reader, Type objectType, int existingValue, bool hasExistingValue, JsonSerializer serializer)
if (reader.TokenType == JsonToken.Integer)
return -Convert.ToInt32(reader.Value);
throw new JsonSerializationException();
public static void Test()
var example = new Example
ExampleList = new() { 10, 11, 12 },
ExampleDictionary = new()
{ "value1", new() { 10, 11, 12 } },
{ "value2", new() { 20, 21, 22 } },
var json = JsonConvert.SerializeObject(example);
Assert.AreEqual(@"{""ExampleInt"":-10,""ExampleList"":[-10,-11,-12],""ExampleDictionary"":{""value1"":[-10,-11,-12],""value2"":[-20,-21,-22],""value3"":null}}", json);
var example2 = JsonConvert.DeserializeObject<Example>(json);
Assert.AreEqual(example.ExampleInt, example2.ExampleInt);
Assert.IsTrue(example.ExampleList.SequenceEqual(example2.ExampleList));
Assert.IsTrue(example.ExampleDictionary.Values.Where(i => i != null).SelectMany(i => i).SequenceEqual(example2.ExampleDictionary.Values.Where(i => i != null).SelectMany(i => i)));
public static void Main()
Console.WriteLine("Environment version: {0} ({1})", System.Runtime.InteropServices.RuntimeInformation.FrameworkDescription , GetNetCoreVersion());
Console.WriteLine("{0} version: {1}", typeof(JsonSerializer).Assembly.GetName().Name, 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];