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 IntStack : Stack<int> { }
public class StringStack : Stack<string> { }
[JsonConverter(typeof(StackConverter<int>))]
public Stack<int> Stack { get; set; }
public static void Test()
Test<StringStack, string>(new [] { "a", "b", "c" });
Test<IntStack, int>(Enumerable.Range(0, 10));
public static void TestSimple()
var stack = new Stack<int>(new [] { 1, 2, 3 });
var options = new JsonSerializerOptions
Converters = { new StackConverterFactory() },
var json = JsonSerializer.Serialize(stack, options);
var stack2 = JsonSerializer.Deserialize<Stack<int>>(json, options);
var json2 = JsonSerializer.Serialize(stack2, options);
Console.WriteLine("Serialized {0}:", stack);
Console.WriteLine("Round-tripped {0}:", stack);
Console.WriteLine(json2);
Assert.IsTrue(stack.SequenceEqual(stack2));
Assert.IsTrue(json == json2);
static void Test<TStack, TItem>(IEnumerable<TItem> items) where TStack : Stack<TItem>, new()
var options = new JsonSerializerOptions
Converters = { new StackConverterFactory() },
var stack = new TStack();
foreach (var item in items)
var json = JsonSerializer.Serialize(stack, options);
Console.WriteLine("Serialized {0}:", stack);
var stack2 = JsonSerializer.Deserialize<TStack>(json, options);
Assert.IsTrue(stack.SequenceEqual(stack2));
var json2 = JsonSerializer.Serialize(stack2, options);
Console.WriteLine("Round-tripped {0}:", stack);
Console.WriteLine(json2);
Assert.IsTrue(json == json2);
public class StackConverterFactory : JsonConverterFactory
public override bool CanConvert(Type typeToConvert)
return GetStackItemType(typeToConvert) != null;
public override JsonConverter CreateConverter(Type typeToConvert, JsonSerializerOptions options)
var itemType = GetStackItemType(typeToConvert);
var converterType = typeof(StackConverter<,>).MakeGenericType(typeToConvert, itemType);
return (JsonConverter)Activator.CreateInstance(converterType);
static Type GetStackItemType(Type type)
var genType = type.GetGenericTypeDefinition();
if (genType == typeof(Stack<>))
return type.GetGenericArguments()[0];
public class StackConverter<TItem> : StackConverter<Stack<TItem>, TItem>
public class StackConverter<TStack, TItem> : JsonConverter<TStack> where TStack : Stack<TItem>, new()
public override TStack Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
var list = JsonSerializer.Deserialize<List<TItem>>(ref reader, options);
var stack = typeToConvert == typeof(Stack<TItem>) ? (TStack)new Stack<TItem>(list.Count) : new TStack();
for (int i = list.Count - 1; i >= 0; i--)
public override void Write(Utf8JsonWriter writer, TStack value, JsonSerializerOptions options)
writer.WriteStartArray();
foreach (var item in value)
JsonSerializer.Serialize(writer, item, options);
public static void Main()
Console.WriteLine("Environment version: {0} ({1})", System.Runtime.InteropServices.RuntimeInformation.FrameworkDescription , GetNetCoreVersion());
Console.WriteLine("System.Text.Json: " + 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.CodeBase.Split(new[] { '/', '\\' }, StringSplitOptions.RemoveEmptyEntries);
int netCoreAppIndex = Array.IndexOf(assemblyPath, "Microsoft.NETCore.App");
if (netCoreAppIndex > 0 && netCoreAppIndex < assemblyPath.Length - 2)
return assemblyPath[netCoreAppIndex + 1];