Improve exception when number cannot be read as type (#1782)
This commit is contained in:
@@ -0,0 +1,81 @@
|
||||
#region License
|
||||
// Copyright (c) 2007 James Newton-King
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person
|
||||
// obtaining a copy of this software and associated documentation
|
||||
// files (the "Software"), to deal in the Software without
|
||||
// restriction, including without limitation the rights to use,
|
||||
// copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
// copies of the Software, and to permit persons to whom the
|
||||
// Software is furnished to do so, subject to the following
|
||||
// conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be
|
||||
// included in all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
|
||||
// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
|
||||
// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
|
||||
// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
|
||||
// OTHER DEALINGS IN THE SOFTWARE.
|
||||
#endregion
|
||||
|
||||
#if !NET20
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Reflection;
|
||||
using System.Reflection.Emit;
|
||||
using System.Runtime.Serialization;
|
||||
#if !(NET20 || NET35 || NET40 || PORTABLE40)
|
||||
using System.Threading.Tasks;
|
||||
#endif
|
||||
using Newtonsoft.Json.Converters;
|
||||
using Newtonsoft.Json.Linq;
|
||||
using Newtonsoft.Json.Serialization;
|
||||
using Newtonsoft.Json.Utilities;
|
||||
#if DNXCORE50
|
||||
using Xunit;
|
||||
using Test = Xunit.FactAttribute;
|
||||
using Assert = Newtonsoft.Json.Tests.XUnitAssert;
|
||||
#else
|
||||
using NUnit.Framework;
|
||||
#endif
|
||||
|
||||
namespace Newtonsoft.Json.Tests.Issues
|
||||
{
|
||||
[TestFixture]
|
||||
public class Issue1778 : TestFixtureBase
|
||||
{
|
||||
[Test]
|
||||
public void Test()
|
||||
{
|
||||
JsonTextReader reader = new JsonTextReader(new StringReader(@"{""enddate"":-1}"));
|
||||
reader.Read();
|
||||
reader.Read();
|
||||
|
||||
ExceptionAssert.Throws<JsonReaderException>(
|
||||
() => reader.ReadAsDateTime(),
|
||||
"Cannot read number value as type. Path 'enddate', line 1, position 13.");
|
||||
}
|
||||
|
||||
#if !(NET20 || NET35 || NET40 || PORTABLE40)
|
||||
[Test]
|
||||
public async Task Test_Async()
|
||||
{
|
||||
JsonTextReader reader = new JsonTextReader(new StringReader(@"{""enddate"":-1}"));
|
||||
reader.Read();
|
||||
reader.Read();
|
||||
|
||||
await ExceptionAssert.ThrowsAsync<JsonReaderException>(
|
||||
() => reader.ReadAsDateTimeAsync(),
|
||||
"Cannot read number value as type. Path 'enddate', line 1, position 13.");
|
||||
}
|
||||
#endif
|
||||
}
|
||||
}
|
||||
#endif
|
||||
@@ -12,10 +12,13 @@
|
||||
<AssemblyName>Newtonsoft.Json.Tests</AssemblyName>
|
||||
<RootNamespace>Newtonsoft.Json.Tests</RootNamespace>
|
||||
<IsPackable>false</IsPackable>
|
||||
<!-- Workaround for https://github.com/nunit/nunit3-vs-adapter/issues/296 -->
|
||||
<DebugType Condition="'$(TargetFramework)' != '' AND '$(TargetFramework)' != 'netcoreapp1.0' AND '$(TargetFramework)' != 'netcoreapp1.1' AND '$(TargetFramework)' != 'netcoreapp2.0'">Full</DebugType>
|
||||
<!-- Disabled because SourceLink isn't referenced to calculate paths -->
|
||||
<DeterministicSourcePaths>false</DeterministicSourcePaths>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<Service Include="{82a7f48d-3b50-4b1e-b82e-3ada8210c358}" />
|
||||
<None Remove="**\*.orig" />
|
||||
<None Include="large.json">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
|
||||
@@ -1940,236 +1940,247 @@ namespace Newtonsoft.Json
|
||||
bool singleDigit = (char.IsDigit(firstChar) && _stringReference.Length == 1);
|
||||
bool nonBase10 = (firstChar == '0' && _stringReference.Length > 1 && _stringReference.Chars[_stringReference.StartIndex + 1] != '.' && _stringReference.Chars[_stringReference.StartIndex + 1] != 'e' && _stringReference.Chars[_stringReference.StartIndex + 1] != 'E');
|
||||
|
||||
if (readType == ReadType.ReadAsString)
|
||||
switch (readType)
|
||||
{
|
||||
string number = _stringReference.ToString();
|
||||
|
||||
// validate that the string is a valid number
|
||||
if (nonBase10)
|
||||
{
|
||||
try
|
||||
case ReadType.ReadAsString:
|
||||
{
|
||||
if (number.StartsWith("0x", StringComparison.OrdinalIgnoreCase))
|
||||
string number = _stringReference.ToString();
|
||||
|
||||
// validate that the string is a valid number
|
||||
if (nonBase10)
|
||||
{
|
||||
Convert.ToInt64(number, 16);
|
||||
try
|
||||
{
|
||||
if (number.StartsWith("0x", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
Convert.ToInt64(number, 16);
|
||||
}
|
||||
else
|
||||
{
|
||||
Convert.ToInt64(number, 8);
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
throw ThrowReaderError("Input string '{0}' is not a valid number.".FormatWith(CultureInfo.InvariantCulture, number), ex);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
Convert.ToInt64(number, 8);
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
throw ThrowReaderError("Input string '{0}' is not a valid number.".FormatWith(CultureInfo.InvariantCulture, number), ex);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!double.TryParse(number, NumberStyles.Float, CultureInfo.InvariantCulture, out _))
|
||||
{
|
||||
throw ThrowReaderError("Input string '{0}' is not a valid number.".FormatWith(CultureInfo.InvariantCulture, _stringReference.ToString()));
|
||||
}
|
||||
}
|
||||
|
||||
numberType = JsonToken.String;
|
||||
numberValue = number;
|
||||
}
|
||||
else if (readType == ReadType.ReadAsInt32)
|
||||
{
|
||||
if (singleDigit)
|
||||
{
|
||||
// digit char values start at 48
|
||||
numberValue = firstChar - 48;
|
||||
}
|
||||
else if (nonBase10)
|
||||
{
|
||||
string number = _stringReference.ToString();
|
||||
|
||||
try
|
||||
{
|
||||
int integer = number.StartsWith("0x", StringComparison.OrdinalIgnoreCase) ? Convert.ToInt32(number, 16) : Convert.ToInt32(number, 8);
|
||||
|
||||
numberValue = integer;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
throw ThrowReaderError("Input string '{0}' is not a valid integer.".FormatWith(CultureInfo.InvariantCulture, number), ex);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
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()));
|
||||
}
|
||||
}
|
||||
|
||||
numberType = JsonToken.Integer;
|
||||
}
|
||||
else if (readType == ReadType.ReadAsDecimal)
|
||||
{
|
||||
if (singleDigit)
|
||||
{
|
||||
// digit char values start at 48
|
||||
numberValue = (decimal)firstChar - 48;
|
||||
}
|
||||
else if (nonBase10)
|
||||
{
|
||||
string number = _stringReference.ToString();
|
||||
|
||||
try
|
||||
{
|
||||
// decimal.Parse doesn't support parsing hexadecimal values
|
||||
long integer = number.StartsWith("0x", StringComparison.OrdinalIgnoreCase) ? Convert.ToInt64(number, 16) : Convert.ToInt64(number, 8);
|
||||
|
||||
numberValue = Convert.ToDecimal(integer);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
throw ThrowReaderError("Input string '{0}' is not a valid decimal.".FormatWith(CultureInfo.InvariantCulture, number), ex);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
ParseResult parseResult = ConvertUtils.DecimalTryParse(_stringReference.Chars, _stringReference.StartIndex, _stringReference.Length, out decimal value);
|
||||
if (parseResult == ParseResult.Success)
|
||||
{
|
||||
numberValue = value;
|
||||
}
|
||||
else
|
||||
{
|
||||
throw ThrowReaderError("Input string '{0}' is not a valid decimal.".FormatWith(CultureInfo.InvariantCulture, _stringReference.ToString()));
|
||||
}
|
||||
}
|
||||
|
||||
numberType = JsonToken.Float;
|
||||
}
|
||||
else if (readType == ReadType.ReadAsDouble)
|
||||
{
|
||||
if (singleDigit)
|
||||
{
|
||||
// digit char values start at 48
|
||||
numberValue = (double)firstChar - 48;
|
||||
}
|
||||
else if (nonBase10)
|
||||
{
|
||||
string number = _stringReference.ToString();
|
||||
|
||||
try
|
||||
{
|
||||
// double.Parse doesn't support parsing hexadecimal values
|
||||
long integer = number.StartsWith("0x", StringComparison.OrdinalIgnoreCase) ? Convert.ToInt64(number, 16) : Convert.ToInt64(number, 8);
|
||||
|
||||
numberValue = Convert.ToDouble(integer);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
throw ThrowReaderError("Input string '{0}' is not a valid double.".FormatWith(CultureInfo.InvariantCulture, number), ex);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
string number = _stringReference.ToString();
|
||||
|
||||
if (double.TryParse(number, NumberStyles.Float, CultureInfo.InvariantCulture, out double value))
|
||||
{
|
||||
numberValue = value;
|
||||
}
|
||||
else
|
||||
{
|
||||
throw ThrowReaderError("Input string '{0}' is not a valid double.".FormatWith(CultureInfo.InvariantCulture, _stringReference.ToString()));
|
||||
}
|
||||
}
|
||||
|
||||
numberType = JsonToken.Float;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (singleDigit)
|
||||
{
|
||||
// digit char values start at 48
|
||||
numberValue = (long)firstChar - 48;
|
||||
numberType = JsonToken.Integer;
|
||||
}
|
||||
else if (nonBase10)
|
||||
{
|
||||
string number = _stringReference.ToString();
|
||||
|
||||
try
|
||||
{
|
||||
numberValue = number.StartsWith("0x", StringComparison.OrdinalIgnoreCase) ? Convert.ToInt64(number, 16) : Convert.ToInt64(number, 8);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
throw ThrowReaderError("Input string '{0}' is not a valid number.".FormatWith(CultureInfo.InvariantCulture, number), ex);
|
||||
}
|
||||
|
||||
numberType = JsonToken.Integer;
|
||||
}
|
||||
else
|
||||
{
|
||||
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()));
|
||||
if (!double.TryParse(number, NumberStyles.Float, CultureInfo.InvariantCulture, out _))
|
||||
{
|
||||
throw ThrowReaderError("Input string '{0}' is not a valid number.".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
|
||||
numberType = JsonToken.String;
|
||||
numberValue = number;
|
||||
}
|
||||
else
|
||||
break;
|
||||
case ReadType.ReadAsInt32:
|
||||
{
|
||||
if (_floatParseHandling == FloatParseHandling.Decimal)
|
||||
if (singleDigit)
|
||||
{
|
||||
parseResult = ConvertUtils.DecimalTryParse(_stringReference.Chars, _stringReference.StartIndex, _stringReference.Length, out decimal d);
|
||||
// digit char values start at 48
|
||||
numberValue = firstChar - 48;
|
||||
}
|
||||
else if (nonBase10)
|
||||
{
|
||||
string number = _stringReference.ToString();
|
||||
|
||||
try
|
||||
{
|
||||
int integer = number.StartsWith("0x", StringComparison.OrdinalIgnoreCase) ? Convert.ToInt32(number, 16) : Convert.ToInt32(number, 8);
|
||||
|
||||
numberValue = integer;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
throw ThrowReaderError("Input string '{0}' is not a valid integer.".FormatWith(CultureInfo.InvariantCulture, number), ex);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
ParseResult parseResult = ConvertUtils.Int32TryParse(_stringReference.Chars, _stringReference.StartIndex, _stringReference.Length, out int value);
|
||||
if (parseResult == ParseResult.Success)
|
||||
{
|
||||
numberValue = d;
|
||||
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()));
|
||||
}
|
||||
}
|
||||
|
||||
numberType = JsonToken.Integer;
|
||||
}
|
||||
break;
|
||||
case ReadType.ReadAsDecimal:
|
||||
{
|
||||
if (singleDigit)
|
||||
{
|
||||
// digit char values start at 48
|
||||
numberValue = (decimal)firstChar - 48;
|
||||
}
|
||||
else if (nonBase10)
|
||||
{
|
||||
string number = _stringReference.ToString();
|
||||
|
||||
try
|
||||
{
|
||||
// decimal.Parse doesn't support parsing hexadecimal values
|
||||
long integer = number.StartsWith("0x", StringComparison.OrdinalIgnoreCase) ? Convert.ToInt64(number, 16) : Convert.ToInt64(number, 8);
|
||||
|
||||
numberValue = Convert.ToDecimal(integer);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
throw ThrowReaderError("Input string '{0}' is not a valid decimal.".FormatWith(CultureInfo.InvariantCulture, number), ex);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
ParseResult parseResult = ConvertUtils.DecimalTryParse(_stringReference.Chars, _stringReference.StartIndex, _stringReference.Length, out decimal value);
|
||||
if (parseResult == ParseResult.Success)
|
||||
{
|
||||
numberValue = value;
|
||||
}
|
||||
else
|
||||
{
|
||||
throw ThrowReaderError("Input string '{0}' is not a valid decimal.".FormatWith(CultureInfo.InvariantCulture, _stringReference.ToString()));
|
||||
}
|
||||
}
|
||||
|
||||
numberType = JsonToken.Float;
|
||||
}
|
||||
break;
|
||||
case ReadType.ReadAsDouble:
|
||||
{
|
||||
if (singleDigit)
|
||||
{
|
||||
// digit char values start at 48
|
||||
numberValue = (double)firstChar - 48;
|
||||
}
|
||||
else if (nonBase10)
|
||||
{
|
||||
string number = _stringReference.ToString();
|
||||
|
||||
try
|
||||
{
|
||||
// double.Parse doesn't support parsing hexadecimal values
|
||||
long integer = number.StartsWith("0x", StringComparison.OrdinalIgnoreCase) ? Convert.ToInt64(number, 16) : Convert.ToInt64(number, 8);
|
||||
|
||||
numberValue = Convert.ToDouble(integer);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
throw ThrowReaderError("Input string '{0}' is not a valid double.".FormatWith(CultureInfo.InvariantCulture, number), ex);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
string number = _stringReference.ToString();
|
||||
|
||||
if (double.TryParse(number, NumberStyles.Float, CultureInfo.InvariantCulture, out double d))
|
||||
if (double.TryParse(number, NumberStyles.Float, CultureInfo.InvariantCulture, out double value))
|
||||
{
|
||||
numberValue = d;
|
||||
numberValue = value;
|
||||
}
|
||||
else
|
||||
{
|
||||
throw ThrowReaderError("Input string '{0}' is not a valid number.".FormatWith(CultureInfo.InvariantCulture, _stringReference.ToString()));
|
||||
throw ThrowReaderError("Input string '{0}' is not a valid double.".FormatWith(CultureInfo.InvariantCulture, _stringReference.ToString()));
|
||||
}
|
||||
}
|
||||
|
||||
numberType = JsonToken.Float;
|
||||
}
|
||||
}
|
||||
break;
|
||||
case ReadType.Read:
|
||||
case ReadType.ReadAsInt64:
|
||||
{
|
||||
if (singleDigit)
|
||||
{
|
||||
// digit char values start at 48
|
||||
numberValue = (long)firstChar - 48;
|
||||
numberType = JsonToken.Integer;
|
||||
}
|
||||
else if (nonBase10)
|
||||
{
|
||||
string number = _stringReference.ToString();
|
||||
|
||||
try
|
||||
{
|
||||
numberValue = number.StartsWith("0x", StringComparison.OrdinalIgnoreCase) ? Convert.ToInt64(number, 16) : Convert.ToInt64(number, 8);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
throw ThrowReaderError("Input string '{0}' is not a valid number.".FormatWith(CultureInfo.InvariantCulture, number), ex);
|
||||
}
|
||||
|
||||
numberType = JsonToken.Integer;
|
||||
}
|
||||
else
|
||||
{
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
default:
|
||||
throw JsonReaderException.Create(this, "Cannot read number value as type.");
|
||||
}
|
||||
|
||||
ClearRecentString();
|
||||
|
||||
Reference in New Issue
Block a user