我有一个相当大的对象类,它由一组基元属性值(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为我指明了正确的方向。
最自然的方法是将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
来做任何您想做的事情。
在本例中,如果接收到的值是null
或undefined
:,则返回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);
}
}