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

JOG*_*JOG 4 .net c# serialization json.net

我试图解析从json(例如.id: 4.5)到poco 的十进制值int,我想要一个异常.

背景:

Newtonsoft.Json.JsonSerializationException遇到小数时,这种反序列化会抛出,期望int:

httpContent.ReadAsAsync<MyCollection<T>>(
                mediaTypeFormatters,
                cancellationToken);
Run Code Online (Sandbox Code Playgroud)

MyCollection<T>是一个类型列表Result的类T,T可以有一个int.现在,我想抓住扔掉并保留其余部分的那些.所以我首先将它作为一个集合提取出来JObject,然后在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
Run Code Online (Sandbox Code Playgroud)

问题是

即使使用相同的格式化程序,我也无法以这种方式抛出:

这只是反序列化4.5to 4并且不抛出:

var jsonSerializer = JsonSerializer.Create(myMediaTypeFormatters.JsonFormatter.SerializerSettings);
j.ToObject<T>(jsonSerializer)
Run Code Online (Sandbox Code Playgroud)

与此相同:

var ser = myMediaTypeFormatters.JsonFormatter.CreateJsonSerializer();
tObjects.Add(ser.Deserialize<T>(j.CreateReader()));
Run Code Online (Sandbox Code Playgroud)

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

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

IEnumerable<MediaTypeFormatter> mediaTypeFormatters = myMediaTypeFormatters;
Run Code Online (Sandbox Code Playgroud)

问题:

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

dbc*_*dbc 5

Json.NET似乎在如何将浮点JSON值转换为整数方面存在不一致.例如,使用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.

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

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

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

与此同时,您有几个选项可以避免不一致.首先,您可以为尝试将浮点值反序列化为整数时引发异常的整数引入自定义JsonConverter,然后在从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();
    }
}
Run Code Online (Sandbox Code Playgroud)

然后做:

var jsonSerializer = JsonSerializer.Create(myMediaTypeFormatters.JsonFormatter.SerializerSettings;
jsonSerializer.Converters.Add(new StrictIntConverter());    
j.ToObject<T>(jsonSerializer)
Run Code Online (Sandbox Code Playgroud)

样品小提琴#1.

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

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;
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

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