如何让反序列化在期望int的非整数上抛出exception?

我试图将json(例如id: 4.5 )中的十进制值解析为poco int ,我想要一个exception。

背景:

当遇到期望int的小数时,此反序列化会抛出Newtonsoft.Json.JsonSerializationException

 httpContent.ReadAsAsync<MyCollection>( mediaTypeFormatters, cancellationToken); 

MyCollection是一个类,其类型为T Result列表, T可以为int 。 现在,我想抓住那些扔掉并保留其余部分的人。 所以我首先将它作为JObject的集合提取,然后在try-catch中逐个解析它们。

 var jObjectsMyCollection = await httpContent.ReadAsAsync<MyCollection>( mediaTypeFormatters, cancellationToken); foreach (var j in jObjectsMyCollection.Results) { try { // now let's parse j one by one 

问题是

我不能这样做,即使使用相同的格式化程序:

这只是反序列化4.54并且不抛出:

 var jsonSerializer = JsonSerializer.Create(myMediaTypeFormatters.JsonFormatter.SerializerSettings); j.ToObject(jsonSerializer) 

与此相同:

 var ser = myMediaTypeFormatters.JsonFormatter.CreateJsonSerializer(); tObjects.Add(ser.Deserialize(j.CreateReader())); 

为了记录,在不同的两个不同的格式化程序设置如下:

 myMediaTypeFormatters= new MediaTypeFormatterCollection(); myMediaTypeFormatters.JsonFormatter.SerializerSettings.Error += SerializationErrorHander; myMediaTypeFormatters.JsonFormatter.SerializerSettings.ContractResolver = new SnakeCasePropertyNamesContractResolver(); myMediaTypeFormatters.JsonFormatter.SerializerSettings.DateTimeZoneHandling = DateTimeZoneHandling.Utc; IEnumerable mediaTypeFormatters = myMediaTypeFormatters; 

问题:

如何使它与ReadAsAsync完全相同的数据? 我在重复使用MediaTypeFormatters时做错了吗?

Json.NET似乎在如何将浮点JSON值转换为整数方面存在不一致。 例如,使用10.0.2:

  • JsonConvert.DeserializeObject("4.5") 失败
  • JToken.Parse("4.5").ToObject() 成功并返回4。
  • JsonConvert.DeserializeObject("4.5") 成功并返回4。
  • JsonConvert.DeserializeObject("4.5") 成功并返回4。

(实际上,将"4.5"直接反序列化为int似乎是唯一失败的情况.Json.NET将直接或间接地将"4.5"反序列化为任何其他整数类型。)

差异似乎是由JsonTextReader.ParseReadNumber() (在直接反序列化JSON时调用)和JsonReader.ReadAsInt32() (从JToken反序列化时调用JsonTextReader.ParseReadNumber()之间的不一致引起的。 前者检查文本JSON值在反序列化为int时实际上是整数,而后者只是调用Convert.ToInt32() ,它很高兴地返回舍入值。

如果需要,您可以报告有关不一致的问题 。

在此期间,您有几个选项可以避免不一致。 首先,您可以为整数引入一个自定义JsonConverter ,在尝试将浮点值反序列化为整数时抛出exception,然后在从JToken层次结构反序列化时使用它:

 public class StrictIntConverter : StrictIntConverterBase { public override bool CanConvert(Type objectType) { return objectType == typeof(int) || objectType == typeof(int?); } } public abstract class StrictIntConverterBase : JsonConverter { public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer) { Type type = (Nullable.GetUnderlyingType(objectType) ?? objectType); bool isNullable = (Nullable.GetUnderlyingType(objectType) != null); if (reader.TokenType == JsonToken.Null) { if (isNullable) return null; throw new JsonSerializationException(string.Format("Null value for {0}", objectType)); } else if (reader.TokenType == JsonToken.Float) { throw new JsonSerializationException(string.Format("Floating-point value {0} found for {1}.", reader.Value, type)); } else { // Integer or string containing an integer if (reader.Value.GetType() == type) return reader.Value; return JToken.Load(reader).ToObject(type); } } public override bool CanWrite { get { return false; } } public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer) { throw new NotImplementedException(); } } 

然后做:

 var jsonSerializer = JsonSerializer.Create(myMediaTypeFormatters.JsonFormatter.SerializerSettings; jsonSerializer.Converters.Add(new StrictIntConverter()); j.ToObject(jsonSerializer) 

样品小提琴#1 。

另一种选择是使用Json.NET的序列化error handling来捕获和吞噬exception,例如:在MyCollection类型中反序列化集合项时:

 public class MyCollection : Collection { [OnError] void OnError(StreamingContext context, ErrorContext errorContext) { if (errorContext.OriginalObject != this) { // Error occurred deserializing an item in the collection. Swallow it. errorContext.Handled = true; } } } 

这允许您直接反序列化到MyCollection并跳过中间JToken表示。 样品小提琴#2 。