System.Text.Json 将空字符串全局序列化为空字符串

System.Text.Json Serialize null strings into empty strings globally

正在将代码从 newtonsoft json 迁移到 system.text.json

我需要所有可为 null 的字符串呈现为空字符串。

我编写了以下转换器,但所有空字符串值仍呈现为空。

并且对于空字符串值,不调用 Write 方法。永远不会遇到断点。

public class EmptyStringConverter : JsonConverter<string>
{
    public override string Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
        => Convert.ToString(reader.GetString(), CultureInfo.CurrentCulture);

    public override void Write(Utf8JsonWriter writer, string value, JsonSerializerOptions options)
    {
        if (writer == null)
            throw new ArgumentNullException(nameof(writer));
        writer.WriteStringValue(value ?? "");
    }
}

启动代码

services.AddControllers()
    .AddJsonOptions(option =>
    {
        option.JsonSerializerOptions.Converters.Add(new EmptyStringConverter());
    });

控制台示例

class Program
{
    static void Main(string[] args)
    {
        var jsonSerializerOptions = new JsonSerializerOptions();
        jsonSerializerOptions.Converters.Add(new EmptyStringConverter());
        var json = JsonSerializer.Serialize(new Model() { FirstName = null }, jsonSerializerOptions);
    }
}

public class EmptyStringConverter : JsonConverter<string>
{
    public override string Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
        => Convert.ToString(reader.GetString(), CultureInfo.CurrentCulture);

    public override void Write(Utf8JsonWriter writer, string value, JsonSerializerOptions options)
    {
        if (writer == null)
            throw new ArgumentNullException(nameof(writer));
        writer.WriteStringValue(value ?? "");
    }
}

public class Model
{
    public string FirstName { get; set; }
}

试一试

/// <summary>
/// Convert empty to null when read data json
/// </summary>
public class EmptyStringToNullConverter : JsonConverter<string>
{
    /// <summary>
    /// Override CanConvert method of JsonConverter
    /// This instance only convert the string type.
    /// </summary>
    /// <returns></returns>
    public override bool CanConvert(Type typeToConvert)
    {
        return typeToConvert == typeof(string);
    }

    /// <summary>
    /// Override ReadJson method of JsonConverter
    /// Convert string null to empty
    /// </summary>
    /// <returns></returns>
    public override string Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
    {
        string value = (string)reader.GetString();
        return value ?? String.Empty;
    }

    /// <summary>
    /// Override WriteJson method of JsonConverter
    /// </summary>
    public override void Write(Utf8JsonWriter writer, string value, JsonSerializerOptions options)
    {
        throw new NotImplementedException("Unnecessary");
    }
}

在 .NET 5.0 中,这可以通过覆盖 JsonConverter<T>.HandleNull 并返回 true:

来完成
public class EmptyStringConverter : JsonConverter<string>
{
    public override bool HandleNull => true;
    
    public override string Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
        => reader.TokenType == JsonTokenType.Null ? "" : reader.GetString();

    public override void Write(Utf8JsonWriter writer, string value, JsonSerializerOptions options) =>
        writer.WriteStringValue(value ?? "");
}

有关更多信息,请参阅 Handle null values

演示 fiddle here.

在 .NET Core 3.x 中未实现。来自 .NET Core Handle null values 3.x:

Handle null values

By default, the serializer handles null values as follows:

  • For reference types and Nullable<T> types:

    • It does not pass null to custom converters on serialization.
    • It does not pass JsonTokenType.Null to custom converters on deserialization.
    • It returns a null instance on deserialization.
    • It writes null directly with the writer on serialization.
  • For non-nullable value types:

    • It passes JsonTokenType.Null to custom converters on deserialization. (If no custom converter is available, a JsonException exception is thrown by the internal converter for the type.)

This null-handling behavior is primarily to optimize performance by skipping an extra call to the converter. In addition, it avoids forcing converters for nullable types to check for null at the start of every Read and Write method override.