using System.Collections;
using System.Collections.Generic;
using System.Text.Json.Serialization;
using System.Text.Json.Serialization.Metadata;
using System.Text.Json.Nodes;
public static partial class JsonExtensions
public static Action<JsonTypeInfo> AddMemberAwareDoubleConverters { get; } = static typeInfo =>
if (typeInfo.Kind != JsonTypeInfoKind.Object)
foreach (var property in typeInfo.Properties)
if (property.CustomConverter == null && property.GetMemberInfo() is {} memberInfo)
if (property.PropertyType == typeof(double))
property.CustomConverter = new MemberAwareDoubleConverter(memberInfo);
else if (property.PropertyType == typeof(double?))
property.CustomConverter = new MemberAwareNullableDoubleConverter(memberInfo);
public static MemberInfo? GetMemberInfo(this JsonPropertyInfo property) => (property.AttributeProvider as MemberInfo);
class MemberAwareDoubleConverter : JsonConverter<double>
MemberInfo MemberInfo { get; }
public MemberAwareDoubleConverter(MemberInfo memberInfo) => this.MemberInfo = memberInfo ?? throw new ArgumentNullException(nameof(memberInfo));
public override void Write(Utf8JsonWriter writer, double value, JsonSerializerOptions options) =>
writer.WriteRawValue($"{JsonSerializer.Serialize(value)} /* double from {MemberInfo.Name} */", true);
public override double Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) =>
class MemberAwareNullableDoubleConverter : JsonConverter<double?>
public override bool HandleNull => true;
MemberInfo MemberInfo { get; }
public MemberAwareNullableDoubleConverter(MemberInfo memberInfo) => this.MemberInfo = memberInfo ?? throw new ArgumentNullException(nameof(memberInfo));
public override void Write(Utf8JsonWriter writer, double? value, JsonSerializerOptions options) =>
writer.WriteRawValue($"{JsonSerializer.Serialize(value)} /* double? from {MemberInfo.Name} */", true);
public override double? Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) =>
JsonTokenType.Number => reader.GetDouble(),
JsonTokenType.Null => null,
_ => throw new JsonException(),
public double RootDoubleValue { get; set; }
public double? RootNullableValue { get; set; }
public List<Item> Items { get; set; } = new ();
public record Item(double ItemDoubleValue, double? ItemNullableValue);
public static void Test()
RootDoubleValue = 101.01,
RootNullableValue = 202.02,
Items = { new(2101.01, null) },
var options = new JsonSerializerOptions
TypeInfoResolver = new DefaultJsonTypeInfoResolver
Modifiers = { JsonExtensions.AddMemberAwareDoubleConverters },
ReadCommentHandling = JsonCommentHandling.Skip,
var json = JsonSerializer.Serialize(root, options);
var root2 = JsonSerializer.Deserialize<Root>(json, options);
var json2 = JsonSerializer.Serialize(root2, options);
Assert.AreEqual(json, json2);
public static class ObjectExtensions
public static T ThrowOnNull<T>(this T? value) where T : class => value ?? throw new ArgumentNullException();
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: ");