如何让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
相关产品推荐
相关产品推荐

