You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

System.Text.Json序列化无法移除空字符串属性,求解决方案

问题描述

我希望序列化JSON时省略空字符串属性,于是做了以下操作,但实际结果不符合预期——空字符串被转为null后,对应的属性依然以null值出现在JSON中,没有被省略。

我的操作步骤

  1. 创建自定义转换器EmptyStringToNullConverter,将空字符串转换为null:
public class EmptyStringToNullConverter : JsonConverter<string>
{
    public override string? Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
    {
        return reader.GetString();
    }

    public override void Write(Utf8JsonWriter writer, string value, JsonSerializerOptions options)
    {
        if (value == string.Empty)
            writer.WriteNullValue();
        else
            writer.WriteStringValue(value);
    }
}
  1. 编写JsonSerializerClass作为System.Text.Json.JsonSerializer的包装类,添加上述转换器并设置忽略null值:
public class JsonSerializerClass
{
    private JsonSerializerOptions serializeOptions;
    private JsonSerializerOptions deserializeOptions;

    public JsonSerializerClass()
    {
        serializeOptions = new JsonSerializerOptions();
        deserializeOptions = new JsonSerializerOptions();
        InitSerializer();
        InitDeserializer();
    }

    public JsonSerializerOptions SerializeOptions => serializeOptions;

    public string Serialize(object value)
    {
        return Serialize(value, serializeOptions);
    }

    public T Deserialize<T>(string json)
    {
        var o = System.Text.Json.JsonSerializer.Deserialize<T>(json, deserializeOptions);

        if (o == null)
            throw new ArgumentException($"Cannot deserialize JSON to type {typeof(T)}: {json}");

        return o;
    }

    public static string Serialize(object value, JsonSerializerOptions options)
    {
        return System.Text.Json.JsonSerializer.Serialize(value, options);
    }

    private void InitSerializer()
    {
        serializeOptions.Converters.Add(new EmptyStringToNullConverter());

        serializeOptions.DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull;
    }

    private void InitDeserializer()
    {
        deserializeOptions.PropertyNameCaseInsensitive = true;

        deserializeOptions.Converters.Add(new JsonStringEnumConverter());
        deserializeOptions.Converters.Add(new DateOnlyConverter());
        deserializeOptions.Converters.Add(new DateOnlyNullableConverter());
        deserializeOptions.Converters.Add(new IntConverter());
        deserializeOptions.Converters.Add(new DecimalConverter());

        deserializeOptions.IncludeFields = true;
        deserializeOptions.AllowTrailingCommas = true;
        deserializeOptions.NumberHandling = JsonNumberHandling.AllowReadingFromString;
        deserializeOptions.ReadCommentHandling = JsonCommentHandling.Skip;
    }
}
  1. 测试用例:
var person = new Person
{
    Name = "John",
    LastName = "Doe",
    MiddleName = ""
};
var serializer = new JsonSerializerClass();

var json = serializer.Serialize(person);

json.ToLower().Should().NotContain(nameof(person.MiddleName).ToLower());

问题原因

JsonIgnoreCondition.WhenWritingNull的判断逻辑是基于原始属性值,而非转换器处理后的结果。当属性值是空字符串(不是null)时,序列化框架会判定该属性需要被序列化,随后调用自定义转换器将其写入为null,但框架不会再对写入后的null值进行二次检查,因此最终JSON中会保留"MiddleName": null。

解决方案

不需要通过转换器将空字符串转为null,直接在序列化阶段判断并跳过空字符串属性即可。以下是两种可行方案:

方案1:通过JsonTypeInfo自定义序列化条件(.NET 7+)

修改InitSerializer方法,使用DefaultJsonTypeInfoResolver为string类型属性添加序列化判断,当值为空字符串时直接跳过:

private void InitSerializer()
{
    // 移除原有的EmptyStringToNullConverter,不需要再转null
    // serializeOptions.Converters.Add(new EmptyStringToNullConverter());

    serializeOptions.TypeInfoResolver = new DefaultJsonTypeInfoResolver
    {
        Modifiers =
        {
            typeInfo =>
            {
                if (typeInfo.Kind == JsonTypeInfoKind.Object)
                {
                    foreach (var property in typeInfo.Properties)
                    {
                        // 仅针对string类型属性,空字符串时不序列化
                        if (property.PropertyType == typeof(string))
                        {
                            property.ShouldSerialize = (obj, value) => 
                                value is string str && !string.IsNullOrEmpty(str);
                        }
                    }
                }
            }
        }
    };

    // 保留原有null值忽略配置(如果需要忽略null的string属性)
    serializeOptions.DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull;
}

方案2:序列化前批量将空字符串转为null

如果项目使用的是低版本.NET(低于7),可以通过反射遍历对象属性,将所有空字符串赋值为null,再进行序列化:

// 添加一个工具方法,遍历对象属性将空字符串设为null
private void SetEmptyStringsToNull(object obj)
{
    if (obj == null) return;
    var properties = obj.GetType().GetProperties(BindingFlags.Public | BindingFlags.Instance);
    foreach (var prop in properties)
    {
        if (prop.PropertyType == typeof(string))
        {
            var value = (string?)prop.GetValue(obj);
            if (value == string.Empty)
            {
                prop.SetValue(obj, null);
            }
        }
        // 如果有嵌套对象,可以递归处理
        else if (prop.PropertyType.IsClass && prop.PropertyType != typeof(string))
        {
            SetEmptyStringsToNull(prop.GetValue(obj)!);
        }
    }
}

// 修改Serialize方法,先处理空字符串再序列化
public string Serialize(object value)
{
    SetEmptyStringsToNull(value);
    return Serialize(value, serializeOptions);
}

使用方案1或方案2后,测试用例中的MiddleName属性会被直接省略,不会出现在最终的JSON中。


内容的提问来源于stack exchange,提问作者Martijn

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.05 00:00:57