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 record OutputState(bool Active);
public class BoolConverter : JsonConverter<bool>
public override void Write(Utf8JsonWriter writer, bool value, JsonSerializerOptions options) =>
writer.WriteBooleanValue(value);
public override bool Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) =>
JsonTokenType.True => true,
JsonTokenType.False => false,
JsonTokenType.String => bool.TryParse(reader.GetString(), out var b) ? b : throw new JsonException(),
JsonTokenType.Number => reader.TryGetInt64(out long l) ? Convert.ToBoolean(l) : reader.TryGetDouble(out double d) ? Convert.ToBoolean(d) : false,
_ => throw new JsonException(),
public class NullableConverterFactory : JsonConverterFactory
static readonly byte [] Empty = Array.Empty<byte>();
public override bool CanConvert(Type typeToConvert) => Nullable.GetUnderlyingType(typeToConvert) != null;
public override JsonConverter CreateConverter(Type type, JsonSerializerOptions options) =>
(JsonConverter)Activator.CreateInstance(
typeof(NullableConverter<>).MakeGenericType(
new Type[] { Nullable.GetUnderlyingType(type) }),
BindingFlags.Instance | BindingFlags.Public,
args: new object[] { options },
class NullableConverter<T> : JsonConverter<T?> where T : struct
public NullableConverter(JsonSerializerOptions options) {}
public override T? Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
if (reader.TokenType == JsonTokenType.String)
if (reader.ValueTextEquals(Empty))
return JsonSerializer.Deserialize<T>(ref reader, options);
public override void Write(Utf8JsonWriter writer, T? value, JsonSerializerOptions options) =>
JsonSerializer.Serialize(writer, value.Value, options);
public static void Test()
Test("111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111");
Test("11111111111111111111111111111111111111111111111111111111111111111111111111111111111");
Test("11111111111111111111111111111111111111111111111111111111111111111111111111111111111.0");
Test("111111111111111111111111111111111111111111111111111111111111111111111111111111111112222222222222222222.0");
Test("0.00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001");
Test("4.94065645841247E-324");
Test("4.94065645841247E-326");
Test("1.7976931348623157E+308");
Test("1.7976931348623157E+309");
static void Test(string boolValue)
Console.Write("Testing {0}... ", boolValue);
var data = string.Format("{{ \"Active\": {0} }}", boolValue);
var output = TestRecord(data);
Console.WriteLine("Results consistent: {0}", output == null ? "Failed" : output?.ToString());
static bool? TestRecord(string data)
OutputState systemResult = null;
OutputState newtonsoftResult = null;
var options = new JsonSerializerOptions
Converters = { new BoolConverter(), new NullableConverterFactory() },
var s1 = System.Text.Json.JsonSerializer.Deserialize<OutputState>(data, options);
var s2 = Newtonsoft.Json.JsonConvert.DeserializeObject<OutputState>(data);
Assert.AreEqual(newtonsoftResult?.Active, systemResult?.Active, "newtonsoftResult?.Active == systemResult?.Active");
return systemResult?.Active;
static bool? TestNullable(string boolValue)
(bool succeeded, bool? result) systemResult;
(bool succeeded, bool? result) newtonsoftResult;
var options = new JsonSerializerOptions
Converters = { new BoolConverter(), new NullableConverterFactory() },
var s1 = System.Text.Json.JsonSerializer.Deserialize<bool?>(boolValue, options);
systemResult = (true, s1);
systemResult = (false, null);
Console.WriteLine($"System.Text.Json failed: {ex.Message}");
var s2 = Newtonsoft.Json.JsonConvert.DeserializeObject<bool?>(boolValue);
newtonsoftResult = (true, s2);
newtonsoftResult = (false, null);
Console.WriteLine($"Newtonsoft.Json failed: {ex.Message}");
Assert.AreEqual(newtonsoftResult, systemResult, $"{systemResult} == systemResult");
return newtonsoftResult.result;
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];