我有一个相当大的对象类,它由一堆原始属性值(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。我需要做的就是改变我的问题
public bool r3_verified { get; set; }
到
public bool? r3_verified { get; set; }
我留下了那些没有按照我需要传入的值的空值。
感谢@dbc指出我正确的方向。
答案 0 :(得分:3)
最自然的方法是将float
属性定义为nullables:
[JsonProperty(NullValueHandling = NullValueHandling.Ignore)]
public float? r3_value { get; set; }
设置NullValueHandling.Ignore
会阻止在不存在时将属性序列化为JSON。也可以通过设置JsonSerializerSettings.NullValueHandling
来完成此操作,从而避免将属性添加到每个属性。
另一种可能性(我不建议)是定义一个特殊的“sentinal”常量值,表示未定义的浮点值;然后在每个float上设置属性[DefaultValue(Constants.UninitializedFloat)]
以通知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值很可能不会往返。来自docs:
如果涉及浮点数,则值可能不会往返。如果操作将原始浮点数转换为另一种形式,则称一个值为往返,反向操作将转换后的形式转换回浮点数,最终浮点数等于原始浮点数点数。往返可能会失败,因为转换中丢失或更改了一个或多个最低有效数字。
因此,您的sentinal可能会稍微变圆,并且似乎成为初始值。
答案 1 :(得分:0)
你可以扩展JsonConverter
来做你想做的任何事情。
在此示例中,如果收到的值为float.NaN
或null
,我将返回undefined
:
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);
}
}