【问题标题】:deserialize double.NaN as null with newtonsoft json使用 newtonsoft json 将 double.NaN 反序列化为 null
【发布时间】:2023-03-18 02:40:01
【问题描述】:

鉴于这个类:

public class Foo
{
    public double? Bar { get; set; }
}

要将 double.NaN 序列化为 null,它可以工作:

var foo = new Foo { Bar = double.NaN };
var test = JsonConvert.SerializeObject(foo, new JsonSerializerSettings { FloatFormatHandling = FloatFormatHandling.DefaultValue });
// {"Bar":null} // ok

但要将 NaN 反序列化为 null,它不起作用:

var test2 = JsonConvert.SerializeObject(foo);
// {"Bar":"NaN"}
var test3 = JsonConvert.DeserializeObject<Foo>(test2, new JsonSerializerSettings { FloatFormatHandling = FloatFormatHandling.DefaultValue });
// test3.Bar = NaN // Not OK, I want it to be null

有没有比创建自己的自定义转换器更简单的解决方案? https://stackoverflow.com/a/13801482/717058

【问题讨论】:

  • 行为正确且合乎逻辑:NaN 的反序列化将产生 NaN。我认为 null 在 Json 中产生一个空字符串。使用自定义转换器!
  • 反序列化后的test3.Bar是什么?

标签: c# json.net .net-5


【解决方案1】:

我不明白你为什么不想使用转换器,那将是获得你想要的东西的最不突兀的方式。这里的其他解决方案基本上是对转换进行前/后处理,这是不必要的步骤。最糟糕的是,您正在以不切实际且非常手动的方式修改类。您是否要为每种类型添加OnDeserialized() 方法,以确保每个浮点属性都具有“真实”值?该方法可能应该仅用于一般库开发,而不是像这样一次性使用。这并没有让它“更容易”。


使用转换器,您根本不必修改类,您始终可以在转换时指定并将处理 json 对象内的所有兼容转换(因为您说还有许多其他双重属性)。

JsonConvert.DeserializeObject<Foo>(test2, new NoNanRealConverter());
public class NoNanRealConverter : JsonConverter
{
    public override bool CanConvert(Type objectType)
    {
        var type = Nullable.GetUnderlyingType(objectType) ?? objectType;
        return new[] { typeof(float), typeof(double), typeof(decimal) }.Contains(type);
    }

    public override object? ReadJson(JsonReader reader, Type objectType, object? existingValue, JsonSerializer serializer)
    {
        var nullableBase = Nullable.GetUnderlyingType(objectType);
        var type = nullableBase ?? objectType;
        if (nullableBase != null && reader.TokenType == JsonToken.Null)
            return null;
        if (type == typeof(double))
        {
            var value = Convert.ToDouble(reader.Value);
            return Double.IsNaN(value) ? null : value;
        }
        else if (type == typeof(float))
        {
            var value = Convert.ToSingle(reader.Value);
            return Single.IsNaN(value) ? null : value;
        }
        return Convert.ToDecimal(reader.Value);
    }

    public override void WriteJson(JsonWriter writer, object? value, JsonSerializer serializer)
    {
        if (value == null)
            writer.WriteNull();
        else if (value is double d && Double.IsNaN(d))
            writer.WriteNull();
        else if (value is float f && Single.IsNaN(f))
            writer.WriteNull();
        else
            writer.WriteValue(value);
    }
}

如果您想更好地控制转换过程,您可以制作自定义合同解析器。

【讨论】:

    【解决方案2】:

    试试这个:

    public class Foo
    {
        public double? Bar { get; set; }
    
        [OnDeserialized]
        private void OnDeserialized(StreamingContext context)
        {
            if (Bar == double.NaN)
                Bar = null;
        }
    }
    

    参考:Serialization Attributes

    【讨论】:

    • 不错的一个。如果 Foo 类中有多个属性为“double”,我们能比为每个属性添加一个“if”更好吗?例如: if (Bar == double.NaN) Bar = null; if (Bar2 == double.NaN) Bar2 = null;等等……
    • @John 当然。您可以将任何您想要的逻辑添加到方法OnDeserialized
    • 是的,我的意思是,如果我的类 Foo 中有太多属性作为“双”类型。在方法 OnDeserialized 中为它们中的每一个添加逻辑是很麻烦的。如果有人在 Foo 类中添加一个新的 Double 字段,他可能会忘记在方法 OnDeserialized 上添加逻辑。有没有更好的解决方案?
    • 如果你想为 double 类型的每个属性实现这一点?由类定义(以及可选的任何从它派生的类),您可以使用反射,但这将是另一个问题。
    【解决方案3】:

    正如@Martin.Martinsson 注意到的“行为正确且合乎逻辑:NaN 的反序列化将产生 NaN”,null 将产生 null。如果由于某些原因不喜欢它,最简单的方法是使用字符串函数将 NaN 替换为 null

    jsonString=jsonString.Replace(":NaN",":null");
    

    或者你可以在类中添加 OnDeserialized 方法

    public class Foo
    {
    .....
      [OnDeserialized]
      private void OnDeserializedBar(StreamingContext context)
      {
       if (Bar.ToString()  == Double.NaN.ToString()) Bar=null;
       }
    }
    

    我认为你甚至可以同时使用它们。

    【讨论】:

    • 这不是一个好主意。您可以替换 jsonString 中的许多“误报”,它很可能不会仅包含具有该单个属性的给定示例类的实例。
    • @Steeeve 这只是一个提示,因为 PO 没有发布真正的 json。但它正在工作。但我测试了你的代码。它不工作。
    • @Serge 真正的 json 可能包含多个字段作为双精度类型
    • @John,没关系,都会改的。或者,如果您只想更改某些内容,则必须添加一个名称来替换。
    • @Serge 如果有人在 Foo 类中添加了一个新的 Double 字段,他可能会忘记在方法 OnDeserialized 上添加逻辑。有没有更好的解决方案说:这个类中的所有双字段都不能以 NaN 作为值并被 null 替换?
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-06-06
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多