如何使用 System.Text.Json 将所有 `Nullable<T>` 值类型的空字符串反序列化为空值?

How to deserialize an empty string to a null value for all `Nullable<T>` value types using System.Text.Json?

在 .Net Core 3.1 中并使用 System.Text.Json 库,我遇到了一个在 Newtonsoft 库中没有出现的问题。

如果我在 JSON 中为某些类型的属性(在后端键入)DateTime?int? 发送一个空字符串,它 returns 400 状态代码带有值无法反序列化的错误消息。但是,对于任何 Nullable<T>.

,Newtonsoft 都会自动将空字符串解释为空值

一个最小的例子是:

var json = "\"\"";

Assert.AreEqual(null, Newtonsoft.Json.JsonConvert.DeserializeObject<DateTime?>(json)); // Passes
Assert.AreEqual(null, System.Text.Json.JsonSerializer.Deserialize<DateTime?>(json));   // Throws System.Text.Json.JsonException: The JSON value could not be converted to System.Nullable`1[System.DateTime].

有什么方法可以使 System.Text.Json 以同样的方式运行吗?演示 here.

您可以使用 factory converter pattern to create a JsonConverterFactory 将空字符串解释为所有 Nullable<T> 类型值的 null

以下工厂做这项工作:

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,
            binder: null,
            args: new object[] { options },
            culture: null);

    class NullableConverter<T> : JsonConverter<T?> where T : struct
    {
        // DO NOT CACHE the return of (JsonConverter<T>)options.GetConverter(typeof(T)) as DoubleConverter.Read() and DoubleConverter.Write()
        // DO NOT WORK for nondefault values of JsonSerializerOptions.NumberHandling which was introduced in .NET 5
        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 null;
            }
            return JsonSerializer.Deserialize<T>(ref reader, options);
        }           

        public override void Write(Utf8JsonWriter writer, T? value, JsonSerializerOptions options) =>
            JsonSerializer.Serialize(writer, value.Value, options);
    }
}

应将工厂添加到框架的 JsonSerializerOptions.Converters 集合中。

备注:

演示 fiddle here.