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

如何让Json.NET转换器生成的null值属性不被序列化?

问题:Json.NET转换器生成null时,NullValueHandling.Ignore不生效

需求

使用json.net进行JSON序列化时:

  • 将无效的double值(如double.NaN)转换为null。
  • 将double值格式化为短格式(G3)。
  • 不输出值为null的属性。

常规方案的问题

前两个需求可通过自定义Json转换器实现,第三个需求可通过设置JsonSerializerSettings的NullValueHandling = NullValueHandling.Ignore完成,但两者无法兼容:原始值为null时转换器不会被调用,这没问题;但如果是转换器生成null,忽略null值的设置不会生效。

尝试实现

测试代码如下:

static void Main(string[] args)
{
    var test = new A(
        d: 1.23456789,
        nd1: 1.23456789,
        nd2: null,
        nd3: double.NaN,
        da: [1.23456789],
        nda: [1.23456789, null, double.NaN]);

    var json = JsonConvert.SerializeObject(test, new JsonSerializerSettings
    {
        NullValueHandling = NullValueHandling.Ignore,
        Converters = { new MyJsonConverter() }
    });

    Console.WriteLine(json);
}

public class MyJsonConverter : JsonConverter
{
    public override bool CanConvert(Type objectType) => objectType == typeof(double) || objectType == typeof(double?);

    public override void WriteJson(JsonWriter writer, object? value, JsonSerializer serializer)
    {
        if (value is double input && double.IsFinite(input))
            writer.WriteRawValue(input.ToString("G3", NumberFormatInfo.InvariantInfo));
        else
            writer.WriteNull();
    }

    public override object? ReadJson(JsonReader reader, Type objectType, object? existingValue, JsonSerializer serializer) => throw new NotImplementedException();
}

public record A(
    double d,
    double? nd1,
    double? nd2,
    double? nd3,
    double[] da,
    double?[] nda)
{ }

当前输出:

{"d":1.23,"nd1":1.23,"nd3":null,"da":[1.23],"nda":[1.23,null,null]}

期望输出(不包含nd3属性):

{"d":1.23,"nd1":1.23,"da":[1.23],"nda":[1.23,null,null]}

解决方案

要解决这个问题,需结合**自定义合约解析器(ContractResolver)**与转换器,让合约解析器判断属性经过转换器处理后是否为null,再决定是否序列化该属性。

完整实现代码

static void Main(string[] args)
{
    var settings = new JsonSerializerSettings
    {
        NullValueHandling = NullValueHandling.Ignore,
        Converters = { new MyDoubleConverter() },
        ContractResolver = new CustomContractResolver()
    };

    var test = new A(
        d: 1.23456789,
        nd1: 1.23456789,
        nd2: null,
        nd3: double.NaN,
        da: [1.23456789],
        nda: [1.23456789, null, double.NaN]);

    var json = JsonConvert.SerializeObject(test, settings);
    Console.WriteLine(json);
}

public class MyDoubleConverter : JsonConverter<double?>
{
    public override double? ReadJson(JsonReader reader, Type objectType, double? existingValue, bool hasExistingValue, JsonSerializer serializer)
    {
        throw new NotImplementedException();
    }

    public override void WriteJson(JsonWriter writer, double? value, JsonSerializer serializer)
    {
        if (value.HasValue && double.IsFinite(value.Value))
        {
            writer.WriteRawValue(value.Value.ToString("G3", NumberFormatInfo.InvariantInfo));
        }
        else
        {
            writer.WriteNull();
        }
    }

    public override bool CanConvert(Type objectType)
    {
        return objectType == typeof(double) || base.CanConvert(objectType);
    }

    // 供合约解析器调用,判断值是否为无效值(需转null)
    public bool IsNullOrInvalid(double? value)
    {
        return !value.HasValue || !double.IsFinite(value.Value);
    }
}

public class CustomContractResolver : DefaultContractResolver
{
    protected override JsonProperty CreateProperty(MemberInfo member, MemberSerialization memberSerialization)
    {
        var property = base.CreateProperty(member, memberSerialization);

        // 针对double/double?类型属性处理
        if (property.PropertyType == typeof(double) || property.PropertyType == typeof(double?))
        {
            var converter = property.Converter as MyDoubleConverter ??
                            property.ContractConverter as MyDoubleConverter;

            if (converter != null)
            {
                // 自定义ShouldSerialize逻辑:判断值是否为无效值,是则不序列化
                property.ShouldSerialize = instance =>
                {
                    var value = property.ValueProvider.GetValue(instance);
                    if (property.PropertyType == typeof(double))
                    {
                        return !converter.IsNullOrInvalid((double)value);
                    }
                    else
                    {
                        return !converter.IsNullOrInvalid((double?)value);
                    }
                };
            }
        }

        return property;
    }
}

public record A(
    double d,
    double? nd1,
    double? nd2,
    double? nd3,
    double[] da,
    double?[] nda)
{ }

关键说明

  • MyDoubleConverter新增IsNullOrInvalid方法,统一判断double值是否为无效值(NaN、无穷大)或null。
  • CustomContractResolver在创建属性时,为double类型属性设置ShouldSerialize逻辑:若属性值经转换器判断为无效值,则不序列化该属性。
  • 数组中的null元素不受影响,符合期望输出(NullValueHandling.Ignore仅作用于对象属性,不影响数组元素)。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.18 22:21:08