如何让反序列化在期望int的非整数上引发异常? [英] How to let deserialization throw exception on non-integer where expecting an int?

查看:66
本文介绍了如何让反序列化在期望int的非整数上引发异常?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在尝试将json(例如id: 4.5)中的十进制值解析为poco int,我想要一个例外.

I am trying to parse a decimal value from json (eg. id: 4.5) to a poco int and I want an exception.

在遇到期望int的小数时,此反序列化将引发Newtonsoft.Json.JsonSerializationException:

This deserialization throws Newtonsoft.Json.JsonSerializationException when encountering a decimal where expecting an int:

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

MyCollection<T>是具有类型为T的列表Result的类,并且T可以具有int.现在,我想抓住那些抛出的东西,并保留其余的东西.因此,我首先将其提取为JObject的集合,然后在try-catch中逐个解析它们.

MyCollection<T> is a class with a list Result of type T, and T can have an int. Now, I want to catch the ones that throw and keep the rest. So I first extract it as a collection of JObject instead, and then parse them one by one in a try-catch.

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

问题是

即使使用相同的格式化程序,我也无法将其抛出:

The problem is

I can not make it throw this way, even using the same formatter:

这只是将4.5反序列化为4而不会抛出:

This just deserializes the 4.5 to 4 and does not throw:

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

与此相同:

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

为便于记录,在不同的地方使用了两个不同的格式化程序,如下所示:

For the record, the two different formatters used in the different are set up like this:

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

IEnumerable<MediaTypeFormatter> mediaTypeFormatters = myMediaTypeFormatters;

问题:

如何使它与ReadAsAsync完全相同地抛出数据? 重用MediaTypeFormatters时我做错了什么吗?

The Question:

How can I make it throw at exactly the same data as ReadAsAsync does? Am I doing soemthing wrong in reusing the MediaTypeFormatters?

推荐答案

Json.NET似乎在将浮点JSON值转换为整数的方式上存在不一致之处.例如,使用10.0.2:

Json.NET does seem to have inconsistencies in how it converts floating-point JSON values to integers. E.g., using 10.0.2:

  • JsonConvert.DeserializeObject<int>("4.5") 失败.
  • JToken.Parse("4.5").ToObject<int>() 成功并返回4.
  • JsonConvert.DeserializeObject<uint>("4.5") 成功并返回4.
  • JsonConvert.DeserializeObject<long>("4.5") 成功并返回4.
  • JsonConvert.DeserializeObject<int>("4.5") fails.
  • JToken.Parse("4.5").ToObject<int>() succeeds and returns 4.
  • JsonConvert.DeserializeObject<uint>("4.5") succeeds and returns 4.
  • JsonConvert.DeserializeObject<long>("4.5") succeeds and returns 4.

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

(In fact, directly deserializing "4.5" to an int seems to be the only case that fails. Json.NET will deserialize "4.5" directly or indirectly to any other integral type.)

差异似乎是由

The difference seems to arise from inconsistencies between JsonTextReader.ParseReadNumber() (which is called when deserializing JSON directly) and JsonReader.ReadAsInt32() (which is called when deserializing from a JToken). The former checks that the textual JSON value is really an integer when deserializing to an int while the latter simply calls Convert.ToInt32() which happily returns a rounded value.

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

在此期间,您有两种选择可以避免不一致.首先,您可以引入自定义JsonConverter 来抛出一个整数尝试将浮点值反序列化为整数时发生异常,然后从JToken层次结构反序列化时使用该异常:

In the meantime, you have a couple of options to avoid the inconsistency. Firstly, you could introduce a custom JsonConverter for integers that throws an exception when trying to deserialize a floating point value to an integer, then use that when deserializing from a JToken hierarchy:

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<T>(jsonSerializer)

示例小提琴#1 .

另一种选择是使用Json.NET的序列化错误处理MyCollection<T>类型内反序列化收集项时捕获和吞入异常,例如:

Another option would be to use Json.NET's serialization error handling to catch and swallow exceptions when deserializing collection items inside your MyCollection<T> type, for instance:

public class MyCollection<T> : Collection<T>
{
    [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<T>,并跳过中间的JToken表示形式.样本小提琴#2 .

This allows you to deserialize directly to your MyCollection<T> and skip the intermediate JToken representation. Sample fiddle #2.

这篇关于如何让反序列化在期望int的非整数上引发异常?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆