diff --git a/Src/Newtonsoft.Json.Tests/JsonTextReaderTest.cs b/Src/Newtonsoft.Json.Tests/JsonTextReaderTest.cs index 5ed72bb9..853daeae 100644 --- a/Src/Newtonsoft.Json.Tests/JsonTextReaderTest.cs +++ b/Src/Newtonsoft.Json.Tests/JsonTextReaderTest.cs @@ -61,6 +61,95 @@ namespace Newtonsoft.Json.Tests [TestFixture] public class JsonTextReaderTest : TestFixtureBase { + [Test] + public void Float_NaN_Read() + { + const string testJson = "{float: NaN}"; + + JsonTextReader reader = new JsonTextReader(new StringReader(testJson)); + + Assert.IsTrue(reader.Read()); + Assert.IsTrue(reader.Read()); + Assert.IsTrue(reader.Read()); + + Assert.AreEqual(JsonToken.Float, reader.TokenType); + Assert.AreEqual(double.NaN, reader.Value); + + Assert.IsTrue(reader.Read()); + Assert.IsFalse(reader.Read()); + } + + [Test] + public void Float_NaN_ReadAsInt32() + { + const string testJson = "{float: NaN}"; + + JsonTextReader reader = new JsonTextReader(new StringReader(testJson)); + + Assert.IsTrue(reader.Read()); + Assert.IsTrue(reader.Read()); + + ExceptionAssert.Throws(() => reader.ReadAsInt32(), "Cannot read NaN value. Path 'float', line 1, position 11."); + } + + [Test] + public void Float_NaNAndInifinity_ReadAsDouble() + { + const string testJson = @"[ + NaN, + Infinity, + -Infinity +]"; ; + + JsonTextReader reader = new JsonTextReader(new StringReader(testJson)); + + Assert.IsTrue(reader.Read()); + + Assert.AreEqual(double.NaN, reader.ReadAsDouble()); + Assert.AreEqual(JsonToken.Float, reader.TokenType); + Assert.AreEqual(double.NaN, reader.Value); + + Assert.AreEqual(double.PositiveInfinity, reader.ReadAsDouble()); + Assert.AreEqual(JsonToken.Float, reader.TokenType); + Assert.AreEqual(double.PositiveInfinity, reader.Value); + + Assert.AreEqual(double.NegativeInfinity, reader.ReadAsDouble()); + Assert.AreEqual(JsonToken.Float, reader.TokenType); + Assert.AreEqual(double.NegativeInfinity, reader.Value); + + Assert.IsTrue(reader.Read()); + Assert.IsFalse(reader.Read()); + } + + [Test] + public void Float_NaNAndInifinity_ReadAsString() + { + const string testJson = @"[ + NaN, + Infinity, + -Infinity +]"; ; + + JsonTextReader reader = new JsonTextReader(new StringReader(testJson)); + + Assert.IsTrue(reader.Read()); + + Assert.AreEqual(JsonConvert.NaN, reader.ReadAsString()); + Assert.AreEqual(JsonToken.String, reader.TokenType); + Assert.AreEqual(JsonConvert.NaN, reader.Value); + + Assert.AreEqual(JsonConvert.PositiveInfinity, reader.ReadAsString()); + Assert.AreEqual(JsonToken.String, reader.TokenType); + Assert.AreEqual(JsonConvert.PositiveInfinity, reader.Value); + + Assert.AreEqual(JsonConvert.NegativeInfinity, reader.ReadAsString()); + Assert.AreEqual(JsonToken.String, reader.TokenType); + Assert.AreEqual(JsonConvert.NegativeInfinity, reader.Value); + + Assert.IsTrue(reader.Read()); + Assert.IsFalse(reader.Read()); + } + [Test] public void FloatParseHandling_ReadAsString() { @@ -486,7 +575,7 @@ third line", jsonTextReader.Value); Assert.IsTrue(reader.Read()); Assert.AreEqual(JsonToken.StartArray, reader.TokenType); - ExceptionAssert.Throws(() => reader.Read(), "Cannot read NaN as a decimal."); + ExceptionAssert.Throws(() => reader.Read(), "Cannot read NaN value. Path '', line 1, position 4."); } [Test] diff --git a/Src/Newtonsoft.Json/JsonTextReader.cs b/Src/Newtonsoft.Json/JsonTextReader.cs index b49efcd5..0a8600af 100644 --- a/Src/Newtonsoft.Json/JsonTextReader.cs +++ b/Src/Newtonsoft.Json/JsonTextReader.cs @@ -598,6 +598,15 @@ namespace Newtonsoft.Json throw new ArgumentOutOfRangeException(nameof(readType)); } case '-': + if (EnsureChars(1, true) && _chars[_charPos + 1] == 'I') + { + return ParseNumberNegativeInfinity(readType); + } + else + { + ParseNumber(readType); + return Value; + } case '.': case '0': case '1': @@ -630,6 +639,10 @@ namespace Newtonsoft.Json } SetToken(JsonToken.String, expected); return expected; + case 'I': + return ParseNumberPositiveInfinity(readType); + case 'N': + return ParseNumberNaN(readType); case 'n': HandleNull(); return null; @@ -858,7 +871,20 @@ namespace Newtonsoft.Json case 'n': HandleNull(); return null; + case 'N': + return ParseNumberNaN(readType); + case 'I': + return ParseNumberPositiveInfinity(readType); case '-': + if (EnsureChars(1, true) && _chars[_charPos + 1] == 'I') + { + return ParseNumberNegativeInfinity(readType); + } + else + { + ParseNumber(readType); + return Value; + } case '.': case '0': case '1': @@ -1567,15 +1593,15 @@ namespace Newtonsoft.Json } return true; case 'N': - ParseNumberNaN(); + ParseNumberNaN(ReadType.Read); return true; case 'I': - ParseNumberPositiveInfinity(); + ParseNumberPositiveInfinity(ReadType.Read); return true; case '-': if (EnsureChars(1, true) && _chars[_charPos + 1] == 'I') { - ParseNumberNegativeInfinity(); + ParseNumberNegativeInfinity(ReadType.Read); } else { @@ -2293,55 +2319,79 @@ namespace Newtonsoft.Json } } - private void ParseNumberNegativeInfinity() + private object ParseNumberNegativeInfinity(ReadType readType) { if (MatchValueWithTrailingSeparator(JsonConvert.NegativeInfinity)) { - if (_floatParseHandling == FloatParseHandling.Decimal) + switch (readType) { - throw new JsonReaderException("Cannot read -Infinity as a decimal."); + case ReadType.Read: + case ReadType.ReadAsDouble: + if (_floatParseHandling == FloatParseHandling.Double) + { + SetToken(JsonToken.Float, double.NegativeInfinity); + return double.NegativeInfinity; + } + break; + case ReadType.ReadAsString: + SetToken(JsonToken.String, JsonConvert.NegativeInfinity); + return JsonConvert.NegativeInfinity; } - SetToken(JsonToken.Float, double.NegativeInfinity); - } - else - { - throw JsonReaderException.Create(this, "Error parsing negative infinity value."); + throw JsonReaderException.Create(this, "Cannot read -Infinity value."); } + + throw JsonReaderException.Create(this, "Error parsing -Infinity value."); } - private void ParseNumberPositiveInfinity() + private object ParseNumberPositiveInfinity(ReadType readType) { if (MatchValueWithTrailingSeparator(JsonConvert.PositiveInfinity)) { - if (_floatParseHandling == FloatParseHandling.Decimal) + switch (readType) { - throw new JsonReaderException("Cannot read Infinity as a decimal."); + case ReadType.Read: + case ReadType.ReadAsDouble: + if (_floatParseHandling == FloatParseHandling.Double) + { + SetToken(JsonToken.Float, double.PositiveInfinity); + return double.PositiveInfinity; + } + break; + case ReadType.ReadAsString: + SetToken(JsonToken.String, JsonConvert.PositiveInfinity); + return JsonConvert.PositiveInfinity; } - SetToken(JsonToken.Float, double.PositiveInfinity); - } - else - { - throw JsonReaderException.Create(this, "Error parsing positive infinity value."); + throw JsonReaderException.Create(this, "Cannot read Infinity value."); } + + throw JsonReaderException.Create(this, "Error parsing Infinity value."); } - private void ParseNumberNaN() + private object ParseNumberNaN(ReadType readType) { if (MatchValueWithTrailingSeparator(JsonConvert.NaN)) { - if (_floatParseHandling == FloatParseHandling.Decimal) + switch (readType) { - throw new JsonReaderException("Cannot read NaN as a decimal."); + case ReadType.Read: + case ReadType.ReadAsDouble: + if (_floatParseHandling == FloatParseHandling.Double) + { + SetToken(JsonToken.Float, double.NaN); + return double.NaN; + } + break; + case ReadType.ReadAsString: + SetToken(JsonToken.String, JsonConvert.NaN); + return JsonConvert.NaN; } - SetToken(JsonToken.Float, double.NaN); - } - else - { - throw JsonReaderException.Create(this, "Error parsing NaN value."); + throw JsonReaderException.Create(this, "Cannot read NaN value."); } + + throw JsonReaderException.Create(this, "Error parsing NaN value."); } ///