如何让反序列化在期望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.5
到4
并且不抛出:
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。("4.5") -
JsonConvert.DeserializeObject
成功并返回4。("4.5")
(实际上,将"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 。