using System.Collections;
using System.Collections.Generic;
using System.Text.Json.Serialization;
using System.Text.Json.Nodes;
using Newtonsoft.Json.Linq;
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
[JsonConverter(typeof(NullTextValueForNullObjectConverter<Config>))]
public Config Config { get; set; }
public string Message { get; set; }
public string C1 { get; set; }
public string C2 { get; set; }
public sealed class NullTextValueForNullObjectConverter<T> : DefaultConverterFactory<T> where T : class
const string NullValue = "null";
protected override T Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions modifiedOptions)
if (reader.TokenType == JsonTokenType.String)
var s = reader.GetString();
if (string.Equals(s, NullValue, StringComparison.OrdinalIgnoreCase))
return (T)JsonSerializer.Deserialize(ref reader, typeToConvert, modifiedOptions);
public abstract class DefaultConverterFactory<T> : JsonConverterFactory
class DefaultConverter : JsonConverter<T>
readonly JsonSerializerOptions modifiedOptions;
readonly DefaultConverterFactory<T> factory;
public DefaultConverter(JsonSerializerOptions options, DefaultConverterFactory<T> factory)
this.modifiedOptions = options.CopyAndRemoveConverter(factory.GetType());
public override void Write(Utf8JsonWriter writer, T value, JsonSerializerOptions options) => factory.Write(writer, value, modifiedOptions);
public override T Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) => factory.Read(ref reader, typeToConvert, modifiedOptions);
protected virtual T Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions modifiedOptions)
=> (T)JsonSerializer.Deserialize(ref reader, typeToConvert, modifiedOptions);
protected virtual void Write(Utf8JsonWriter writer, T value, JsonSerializerOptions modifiedOptions)
=> JsonSerializer.Serialize(writer, value, modifiedOptions);
public override bool CanConvert(Type typeToConvert) => typeof(T) == typeToConvert;
public override JsonConverter CreateConverter(Type typeToConvert, JsonSerializerOptions options) => new DefaultConverter(options, this);
public static class JsonSerializerExtensions
public static JsonSerializerOptions CopyAndRemoveConverter(this JsonSerializerOptions options, Type converterType)
var copy = new JsonSerializerOptions(options);
for (var i = copy.Converters.Count - 1; i >= 0; i--)
if (copy.Converters[i].GetType() == converterType)
copy.Converters.RemoveAt(i);
public static void Test()
foreach (var jsonWithConfig in GetJson())
Console.WriteLine(jsonWithConfig);
var options = new JsonSerializerOptions
Converters = { new NullTextValueForNullObjectConverter<Config>() },
DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull,
PropertyNameCaseInsensitive = true
var webResponseWithConfig = JsonSerializer.Deserialize<WebResponse>(jsonWithConfig, options);
webResponseWithConfig.Dump();
var json2 = JsonSerializer.Serialize(webResponseWithConfig , options);
Console.WriteLine(json2);
var jsonWithConfigNode = JsonNode.Parse(jsonWithConfig);
var configOriginalJson = JsonSerializer.Serialize(jsonWithConfigNode["config"]);
Assert.AreEqual(webResponseWithConfig.Message, jsonWithConfigNode["message"].ToString());
if (webResponseWithConfig.Config == null)
Assert.That(configOriginalJson == "null" || configOriginalJson == "\"null\"");
else if (webResponseWithConfig.Config != null)
var options2 = new JsonSerializerOptions
Converters = { new NullTextValueForNullObjectConverter<Config>() },
DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull,
PropertyNamingPolicy = JsonNamingPolicy.CamelCase,
Assert.AreEqual(configOriginalJson, JsonSerializer.Serialize(webResponseWithConfig.Config, options2));
static IEnumerable<string> GetJson() =>
{"config":{"c1":"All","c2":"is peachy"},"message":"We found a config object"}
{"config":"null","message":"Config is \"null\""}
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: ");