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;
using System.Text.Json.Serialization.Metadata;
using System.Text.Json.Nodes;
public class JsonNodeListConverter : JsonConverter<IEnumerable<JsonNode?>>
public override bool CanConvert(Type objectType) =>
objectType == typeof(List<JsonNode>) || objectType == typeof(IReadOnlyList<JsonNode>) || objectType == typeof(IReadOnlyCollection<JsonNode>) || objectType == typeof(JsonNode []) || objectType == typeof(IEnumerable<JsonNode>);
public override void Write(Utf8JsonWriter writer, IEnumerable<JsonNode?> value, JsonSerializerOptions options)
writer.WriteStartArray();
foreach (var node in value)
JsonSerializer.Serialize(writer, node, options);
public override IEnumerable<JsonNode?> Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
if (reader.TokenType != JsonTokenType.StartArray)
throw new JsonException();
var list = new List<JsonNode?>();
while (reader.ReadAndAssert().TokenType != JsonTokenType.EndArray)
list.Add(JsonSerializer.Deserialize<JsonNode>(ref reader, options));
return typeToConvert.IsArray ? list.ToArray() : list;
public static class JsonExtensions
public static ref Utf8JsonReader ReadAndAssert(ref this Utf8JsonReader reader) { if (!reader.Read()) { throw new JsonException(); } return ref reader; }
public record Obj(JsonNode value);
public static void Test()
var options = new JsonSerializerOptions
Converters = { new JsonNodeListConverter() },
Test("""{"value":null}""", () =>
JsonSerializer.Deserialize<JsonNode>("null", options)
Test("""{"value":null}""", () =>
JsonSerializer.Deserialize<Obj>("""{"value":null}""", options)
JsonSerializer.Deserialize<List<JsonNode>>("""[1]""", options)
JsonSerializer.Deserialize<List<JsonNode>>("""["a"]""", options)
JsonSerializer.Deserialize<List<JsonNode>>("""[null]""", options)
JsonSerializer.Deserialize<List<JsonNode?>>("""[null]""", options)
JsonSerializer.Deserialize<IReadOnlyList<JsonNode>>("""[null]""", options)
JsonSerializer.Deserialize<JsonNode []>("""[null]""", options)
JsonSerializer.Deserialize<JsonNode? []>("""[null]""", options)
JsonSerializer.Deserialize<IEnumerable<JsonNode>>("""[null]""", options)
Test("""[null, [null], 1, {}, "a"]""", () =>
var list = JsonSerializer.Deserialize<List<JsonNode?>>("""[null, [null], 1, {}, "a"]""",
static void Test<T>(string json, Func<T> test)
var options = new JsonSerializerOptions
Converters = { new JsonNodeListConverter() },
Console.WriteLine("Testing deserialization of {0} to {1}:", json, typeof(T));
Console.WriteLine(" Result: {0}", JsonSerializer.Serialize(value, options));
Console.WriteLine(" FAILED with exception:\n {0}", ex.Message);
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: ");