【问题标题】:Why does JsonConvert deserialize object fail with int but not long为什么JsonConvert反序列化对象以int但不长失败
【发布时间】:2022-11-08 15:09:21
【问题描述】:

在测试在 .Net 6.0 中创建的 av web API 时,我们发现当 API 的用户在 int 上发送十进制数时,您会收到 400 错误,指出由于 int 值上的小数,它无法解析 json。但是对长值做同样的事情效果很好,它只是删除了十进制数字。

因此,为了测试这是否(猜测 MS 使用 Newtonsoft.Json),我制作了一个小 cmd 测试应用程序来测试场景。同样的事情发生在那里 long pareses 丢失小数,并且 int 失败。

那么这是解析器中的错误还是设计错误? [编辑] 它不应该也长期失败吗?

using Newtonsoft.Json;

var data = JsonConvert.DeserializeObject<SomData>(@"{""aInt"":1, ""ALong"":2.2}"); 

Console.WriteLine(data.ALong); // output 2

var data2 = JsonConvert.DeserializeObject<SomData>(@"{""aInt"":1.2, ""ALong"":2}"); // exception

Console.WriteLine(data2.AInt);


internal class SomData
{
   public int AInt { get; set; }
   public long ALong { get; set; }
}

【问题讨论】:

  • 这种不一致似乎是一个错误,Json.NET 对于shortintlongushortuintulong 的行为应该相同。

标签: c# json .net .net-core json.net


【解决方案1】:

答案是设计使然。我们可以看到这个讨论Error converting float value to integer in Web API #654。作者的回答如下。

将浮点值反序列化为整数属性时,更高版本的 Json.NET 会引发错误。

当您使用浮点数时,我总是使用decimal

internal class SomData
{
    public decimal AInt { get; set; }
    public decimal ALong { get; set; }
}

编辑

我看过 Json.Net 的源代码

Int 值将进入else 部分,如下代码来自ReadType.ReadAsInt32 中的JsonTextReader

正如作者设计的那样。

ParseResult parseResult = ConvertUtils.Int32TryParse(_stringReference.Chars, _stringReference.StartIndex, _stringReference.Length, out int value);
if (parseResult == ParseResult.Success)
{
    numberValue = value;
}
else if (parseResult == ParseResult.Overflow)
{
    throw ThrowReaderError("JSON integer {0} is too large or small for an Int32.".FormatWith(CultureInfo.InvariantCulture, _stringReference.ToString()));
}
else
{
    throw ThrowReaderError("Input string '{0}' is not a valid integer.".FormatWith(CultureInfo.InvariantCulture, _stringReference.ToString()));
}

但是让我们看看ReadType.ReadAsInt64 else 部分与ReadAsInt32 有很大不同。

首先,它将到达else let value(object type) 存储为浮点值,如下代码所示。

ParseResult parseResult = ConvertUtils.Int64TryParse(_stringReference.Chars, _stringReference.StartIndex, _stringReference.Length, out long value);
if (parseResult == ParseResult.Success)
{
    numberValue = value;
    numberType = JsonToken.Integer;
}
else if (parseResult == ParseResult.Overflow)
{
#if HAVE_BIG_INTEGER
    string number = _stringReference.ToString();

    if (number.Length > MaximumJavascriptIntegerCharacterLength)
    {
        throw ThrowReaderError("JSON integer {0} is too large to parse.".FormatWith(CultureInfo.InvariantCulture, _stringReference.ToString()));
    }

    numberValue = BigIntegerParse(number, CultureInfo.InvariantCulture);
    numberType = JsonToken.Integer;
#else
    throw ThrowReaderError("JSON integer {0} is too large or small for an Int64.".FormatWith(CultureInfo.InvariantCulture, _stringReference.ToString()));
#endif
}
else
{
    if (_floatParseHandling == FloatParseHandling.Decimal)
    {
        parseResult = ConvertUtils.DecimalTryParse(_stringReference.Chars, _stringReference.StartIndex, _stringReference.Length, out decimal d);
        if (parseResult == ParseResult.Success)
        {
            numberValue = d;
        }
        else
        {
            throw ThrowReaderError("Input string '{0}' is not a valid decimal.".FormatWith(CultureInfo.InvariantCulture, _stringReference.ToString()));
        }
    }
    else
    {
        string number = _stringReference.ToString();

        if (double.TryParse(number, NumberStyles.Float, CultureInfo.InvariantCulture, out double d))
        {
            numberValue = d;
        }
        else
        {
            throw ThrowReaderError("Input string '{0}' is not a valid number.".FormatWith(CultureInfo.InvariantCulture, _stringReference.ToString()));
        }
    }

    numberType = JsonToken.Float;
}

然后数字将由JsonSerializerInternalReader.EnsureType 转换为 Int64

// this won't work when converting to a custom IConvertible
return Convert.ChangeType(value, contract.NonNullableUnderlyingType, culture);

所以我们可以得到long 不会得到异常,但int 会,不知道为什么ReadAsInt64 允许存储为浮动但int 没有。

【讨论】:

  • 它不应该也长期失败吗?
  • @Gaotter 是的,我想这可能是一个错误,我正在查看它的源代码。
  • 我在这里问可能是错误的。我在 git 中提出了一个问题,github.com/JamesNK/Newtonsoft.Json/issues/2664
  • @Gaotter 我在intlong 之间看到了源代码,但不确定它们是否不同,我已经编辑了我的答案
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-06-20
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多