如何防止未定义的值在.Net中序列化为浮点值的0

本文关键字:序列化 Net 何防止 未定义 | 更新日期: 2023-09-27 18:29:03

我有一个相当大的对象类,它由一组基元属性值(int、float、bool、string)定义。我从客户端应用程序中获得一个json字符串,将其反序列化为C#.Net类,以便将它们保存到SQL数据库中。我遇到的问题是,序列化程序为浮点参数提供了一个默认值0,这会破坏我的应用程序,因为未定义的值需要与0的值不同地处理。(注意:如果用户将0值定义为0,则0值是可以接受的,但我不能假设未定义的值为0。)

实际上,我有数百个这样的原始属性,所以我希望有一种方法可以在全局范围内实现这一点,而不必编写自定义的属性类型对象。

以下是我如何将JSON字符串反序列化为C#对象

using System.Web.Script.Serialization; // Note: used to deserialize JSON objects
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
RootObject obj = JsonConvert.DeserializeObject<RootObject>(JSONObjectFromClient);

这是我的对象类

public class SeatDefinition
{
public string DefinitionID { get; set; }
public string r3_tolType { get; set; }
public float r3_value { get; set; } // Note: this could be 0, but it shouldn't be assumed to be 0 if undefined
public bool r3_verified { get; set; }
public float r4_minus { get; set; } // same here
public float r4_plus { get; set; } // and here
public string r4_tolType { get; set; }
public float r4_value { get; set; } //etc
public bool r4_verified { get; set; }
public float r5_minus { get; set; }
public float r5_plus { get; set; }
public string r5_tolType { get; set; }
public float r5_value { get; set; }
public bool r5_verified { get; set; }
// ... 400 more such attributes
}

有人能帮忙吗?

编辑2016-01-05太平洋标准时间晚上11:38原来我是个白痴。如果您在类定义中声明值应该可以为null,那么反序列化程序的自动魔术会将值保留为null。我所需要做的就是改变

public bool r3_verified { get; set; }

public bool? r3_verified { get; set; }

对于那些没有按我需要传递的值,我只剩下空值。

感谢@dbc为我指明了正确的方向。

如何防止未定义的值在.Net中序列化为浮点值的0

最自然的方法是将float属性定义为null:

    [JsonProperty(NullValueHandling = NullValueHandling.Ignore)]
    public float? r3_value { get; set; }

设置NullValueHandling.Ignore可防止属性在不存在时序列化为JSON。也可以通过设置JsonSerializerSettings.NullValueHandling来实现这一点,从而避免了向每个属性添加属性的需要。

另一种可能性(我并不推荐)是定义一个特殊的"sentinal"常数值,它表示一个未定义的浮点值;然后在每个float上设置属性CCD_ 4以将该默认值通知Json.NET;然后设置[JsonProperty(DefaultValueHandling = DefaultValueHandling.IgnoreAndPopulate)]以指示当JSON中缺少属性时,应自动分配默认值:

public static class Constants
{
    public const double UninitializedFloat = float.MinValue;
    public static bool IsUninitialized(this float value)
    {
        return value == UninitializedFloat;
    }
}
public class SeatDefinition
{
    [JsonProperty(DefaultValueHandling = DefaultValueHandling.IgnoreAndPopulate)]
    [DefaultValue(Constants.UninitializedFloat)]
    public float r3_value { get; set; } // Note: this could be 0, but it shouldn't be assumed to be 0 if undefined
}

我不建议这样做,因为如果意外地序列化和反序列化,那么您的重要值可能不会往返。来自文档:

如果涉及浮点数,则值可能不是往返的。如果一个运算将原始浮点数转换为另一种形式,逆运算将转换后的形式转换回浮点数,并且最终的浮点数等于原始浮点数,则称该值为往返值。往返行程可能会失败,因为一个或多个最低有效数字在转换中丢失或更改。

因此,您的sentinal可能会稍微四舍五入,并显示为初始化值。

您可以扩展JsonConverter来做任何您想做的事情。

在本例中,如果接收到的值是nullundefined:,则返回float.NaN

public class CustomFloatConverter : JsonConverter
{
    public override bool CanConvert(Type objectType)
    {
        return objectType == typeof(float);
    }
    public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
    {
        JToken token = JToken.Load(reader);
        if (token.Type == JTokenType.Float)
        {
            return (float)token;
        }
        if (token.Type == JTokenType.String)
        {
            return float.Parse(token.ToString(), CultureInfo.InvariantCulture);
        }
        if (token.Type == JTokenType.Null || token.Type == JTokenType.Undefined)
        {
            return float.NaN;
        }
        throw new JsonSerializationException("Unexpected token type: " + token.Type);
    }
    public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
    {
        throw new NotImplementedException();
    }
}

要使用新的自定义转换器,只需将其添加到JsonConvert在应用程序初始化/引导中的默认设置中即可:

JsonConvert.DefaultSettings = () => new JsonSerializerSettings
{
    Converters = new List<JsonConverter> { new CustomFloatConverter() }
};

因此,给定以下类别:

public class RootObject
{
    public float MyValue { get; set; }
}

这里有一个完整的测试上下文,所有测试都通过了:

[TestClass]
public class UnitTests
{
    [TestInitialize]
    public void Setup()
    {
        JsonConvert.DefaultSettings = () => new JsonSerializerSettings
        {
            Converters = new List<JsonConverter> { new CustomFloatConverter() }
        };
    }
    [TestMethod]
    public void UndefinedIsTreatedAsNan()
    {
        RootObject obj = JsonConvert.DeserializeObject<RootObject>("{MyValue:undefined}");
        Assert.IsTrue(float.IsNaN(obj.MyValue));
    }
    [TestMethod]
    public void NullIsTreatedAsNaN()
    {
        RootObject obj = JsonConvert.DeserializeObject<RootObject>("{MyValue:null}");
        Assert.IsTrue(float.IsNaN(obj.MyValue));
    }
    [TestMethod]
    public void NumbersAreTreatedNormally()
    {
        RootObject obj1 = JsonConvert.DeserializeObject<RootObject>("{MyValue:1.23}");
        RootObject obj2 = JsonConvert.DeserializeObject<RootObject>("{MyValue:0.0}");
        RootObject obj3 = JsonConvert.DeserializeObject<RootObject>("{MyValue:'"1.23'"}");
        Assert.AreEqual(1.23f, obj1.MyValue);
        Assert.AreEqual(0, obj2.MyValue);
        Assert.AreEqual(1.23f, obj3.MyValue);
    }
}