为什么 JsonConvert 反序列化对象失败并返回 int 而不是 long
Why does JsonConvert deserialize object fail with int but not long
测试在 .Net 6.0 中创建的 av web API 时,我们发现当 API 的用户在 int 上发送十进制数时,您会收到 400 错误,说明它无法解析json 因为 int 值上有小数点。但是对长值做同样的事情效果很好,它只是删除了小数。
所以为了测试这个(猜测 MS 使用 Newonsoft.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; }
}
答案是设计使然。我们可以看到这个讨论Error converting float value to integer in Web API #654。作者的回答如下。
Later versions of Json.NET throw an error when deserializing a floating-point value onto an integer property.
当你使用 floating-point 个数字时,我总是使用 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
让值(object
类型)存储为浮点值,如下代码所示。
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
不会得到 execption 但 int
会,不知道为什么 ReadAsInt64
允许存储为浮动但 int
不是。
测试在 .Net 6.0 中创建的 av web API 时,我们发现当 API 的用户在 int 上发送十进制数时,您会收到 400 错误,说明它无法解析json 因为 int 值上有小数点。但是对长值做同样的事情效果很好,它只是删除了小数。
所以为了测试这个(猜测 MS 使用 Newonsoft.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; }
}
答案是设计使然。我们可以看到这个讨论Error converting float value to integer in Web API #654。作者的回答如下。
Later versions of Json.NET throw an error when deserializing a floating-point value onto an integer property.
当你使用 floating-point 个数字时,我总是使用 decimal
。
internal class SomData
{
public decimal AInt { get; set; }
public decimal ALong { get; set; }
}
编辑
我看过Json.Net
的源代码Int 值将进入 else
部分,如下面来自 ReadType.ReadAsInt32
这可能是作者设计的。
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
让值(object
类型)存储为浮点值,如下代码所示。
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
不会得到 execption 但 int
会,不知道为什么 ReadAsInt64
允许存储为浮动但 int
不是。