using System.Collections;
using System.Collections.Generic;
using System.Text.Json.Serialization;
using System.Text.Json.Nodes;
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.Null => false,
_ => throw new JsonException(),
public static partial class JsonExtensions
public static JsonNode? ReplaceFalseWithNullProperties(this JsonNode? root)
foreach (var item in root.DescendantItemsAndSelf(false).Where(i => i.name != null && i.node is JsonValue v && v.TryGetValue<bool>(out var b) && b == false).ToList())
((JsonObject)item.parent!)[item.name!] = null;
public static partial class JsonExtensions
public static IEnumerable<(JsonNode? node, int? index, string? name, JsonNode? parent)> DescendantItemsAndSelf(this JsonNode? root, bool includeSelf = true) =>
RecursiveEnumerableExtensions.Traverse(
(node: root, index: (int?)null, name: (string?)null, parent: (JsonNode?)null),
JsonObject o => o.AsDictionary().Select(p => (p.Value, (int?)null, p.Key.AsNullableReference(), i.node.AsNullableReference())),
JsonArray a => a.Select((item, index) => (item, index.AsNullableValue(), (string?)null, i.node.AsNullableReference())),
_ => i.ToEmptyEnumerable(),
static IEnumerable<T> ToEmptyEnumerable<T>(this T item) => Enumerable.Empty<T>();
static T? AsNullableReference<T>(this T item) where T : class => item;
static Nullable<T> AsNullableValue<T>(this T item) where T : struct => item;
static IDictionary<string, JsonNode?> AsDictionary(this JsonObject o) => o;
public static partial class RecursiveEnumerableExtensions
public static IEnumerable<T> Traverse<T>(
Func<T, IEnumerable<T>> children, bool includeSelf = true)
var stack = new Stack<IEnumerator<T>>();
stack.Push(children(root).GetEnumerator());
var enumerator = stack.Peek();
if (!enumerator.MoveNext())
yield return enumerator.Current;
stack.Push(children(enumerator.Current).GetEnumerator());
foreach (var enumerator in stack)
public class SomeType { }
public sealed class JsonDefinition {
public string? Property { get; set; }
public int? Property2 { get; set; }
public NestedJsonType? Property3 { get; set; }
public bool BoolProperty { get; set; }
public sealed class NestedJsonType{
public SomeType? Property { get; set; }
public static void Test()
Console.WriteLine("Input JSON:");
Console.WriteLine(input);
Console.WriteLine("Preprocessed JsonNode:");
Console.WriteLine(JsonNode.Parse(input, documentOptions : new() { CommentHandling = JsonCommentHandling.Skip }).ReplaceFalseWithNullProperties()?.ToJsonString());
var options = new JsonSerializerOptions
PropertyNamingPolicy = JsonNamingPolicy.CamelCase,
ReadCommentHandling = JsonCommentHandling.Skip,
Converters = { new BoolConverter() },
var model = JsonNode.Parse(input, documentOptions : new() { CommentHandling = JsonCommentHandling.Skip })
.ReplaceFalseWithNullProperties()
.Deserialize<JsonDefinition>(options);
Console.WriteLine("\nRe-serialized {0}:", model);
Console.WriteLine(JsonSerializer.Serialize(model, options));
static string GetJson() =>
""property"": false, // expected type: string
""property2"": false, // expected type: int
""property3"": { // data might be nested
""property"": false // expected type SomeType
""boolProperty"" : false // expected type bool
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: ");