diff --git a/README.md b/README.md index 171044685..800551f60 100644 --- a/README.md +++ b/README.md @@ -89,7 +89,7 @@ And bad JSON will produce an error in `jq` syntax with an additional piece of co ``` scala> """{"curvature": womp}""".fromJson[Banana] -val res: Either[String, Banana] = Left(.curvature(expected a number, got w)) +val res: Either[String, Banana] = Left(.curvature(expected a Double)) ``` Say we extend our data model to include more data types diff --git a/docs/index.md b/docs/index.md index 205c70050..9707491aa 100644 --- a/docs/index.md +++ b/docs/index.md @@ -89,7 +89,7 @@ And bad JSON will produce an error in `jq` syntax with an additional piece of co ``` scala> """{"curvature": womp}""".fromJson[Banana] -val res: Either[String, Banana] = Left(.curvature(expected a number, got w)) +val res: Either[String, Banana] = Left(.curvature(expected a Double)) ``` Say we extend our data model to include more data types diff --git a/zio-json/jvm/src/test/scala/zio/json/DecoderPlatformSpecificSpec.scala b/zio-json/jvm/src/test/scala/zio/json/DecoderPlatformSpecificSpec.scala index 10cfacfae..9e07b5517 100644 --- a/zio-json/jvm/src/test/scala/zio/json/DecoderPlatformSpecificSpec.scala +++ b/zio-json/jvm/src/test/scala/zio/json/DecoderPlatformSpecificSpec.scala @@ -262,11 +262,11 @@ object DecoderPlatformSpecificSpec extends ZIOSpecDefault { test("test hand-coded alternative in `orElse` comment") { val decoder: JsonDecoder[AnyVal] = JsonDecoder.peekChar[AnyVal] { case 't' | 'f' => JsonDecoder[Boolean].widen - case c => JsonDecoder[Int].widen + case _ => JsonDecoder[Int].widen } assert(decoder.decodeJson("true"))(equalTo(Right(true.asInstanceOf[AnyVal]))) && assert(decoder.decodeJson("42"))(equalTo(Right(42.asInstanceOf[AnyVal]))) && - assert(decoder.decodeJson("\"a string\""))(equalTo(Left("(expected a number, got 'a')"))) + assert(decoder.decodeJson("\"a string\""))(equalTo(Left("(expected an Int)"))) } ) ) diff --git a/zio-json/shared/src/main/scala/zio/json/internal/lexer.scala b/zio-json/shared/src/main/scala/zio/json/internal/lexer.scala index 0d9fb2587..ba7b7b5c9 100644 --- a/zio-json/shared/src/main/scala/zio/json/internal/lexer.scala +++ b/zio-json/shared/src/main/scala/zio/json/internal/lexer.scala @@ -286,7 +286,8 @@ object Lexer { } def byte(trace: List[JsonError], in: RetractReader): Byte = { - checkNumber(trace, in) + in.nextNonWhitespace() + in.retract() try { val i = UnsafeNumbers.byte_(in, false) in.retract() @@ -297,7 +298,8 @@ object Lexer { } def short(trace: List[JsonError], in: RetractReader): Short = { - checkNumber(trace, in) + in.nextNonWhitespace() + in.retract() try { val i = UnsafeNumbers.short_(in, false) in.retract() @@ -308,7 +310,8 @@ object Lexer { } def int(trace: List[JsonError], in: RetractReader): Int = { - checkNumber(trace, in) + in.nextNonWhitespace() + in.retract() try { val i = UnsafeNumbers.int_(in, false) in.retract() @@ -319,7 +322,8 @@ object Lexer { } def long(trace: List[JsonError], in: RetractReader): Long = { - checkNumber(trace, in) + in.nextNonWhitespace() + in.retract() try { val i = UnsafeNumbers.long_(in, false) in.retract() @@ -333,7 +337,8 @@ object Lexer { trace: List[JsonError], in: RetractReader ): java.math.BigInteger = { - checkNumber(trace, in) + in.nextNonWhitespace() + in.retract() try { val i = UnsafeNumbers.bigInteger_(in, false, NumberMaxBits) in.retract() @@ -344,7 +349,8 @@ object Lexer { } def float(trace: List[JsonError], in: RetractReader): Float = { - checkNumber(trace, in) + in.nextNonWhitespace() + in.retract() try { val i = UnsafeNumbers.float_(in, false, NumberMaxBits) in.retract() @@ -355,7 +361,8 @@ object Lexer { } def double(trace: List[JsonError], in: RetractReader): Double = { - checkNumber(trace, in) + in.nextNonWhitespace() + in.retract() try { val i = UnsafeNumbers.double_(in, false, NumberMaxBits) in.retract() @@ -369,7 +376,8 @@ object Lexer { trace: List[JsonError], in: RetractReader ): java.math.BigDecimal = { - checkNumber(trace, in) + in.nextNonWhitespace() + in.retract() try { val i = UnsafeNumbers.bigDecimal_(in, false, NumberMaxBits) in.retract() @@ -379,15 +387,6 @@ object Lexer { } } - // really just a way to consume the whitespace - private def checkNumber(trace: List[JsonError], in: RetractReader): Unit = { - (in.nextNonWhitespace(): @switch) match { - case '-' | '0' | '1' | '2' | '3' | '4' | '5' | '6' | '7' | '8' | '9' => () - case c => error("a number,", c, trace) - } - in.retract() - } - // optional whitespace and then an expected character @inline def char(trace: List[JsonError], in: OneCharReader, c: Char): Unit = { val got = in.nextNonWhitespace() diff --git a/zio-json/shared/src/test/scala/zio/json/DecoderSpec.scala b/zio-json/shared/src/test/scala/zio/json/DecoderSpec.scala index 6c15d929c..aac714d8e 100644 --- a/zio-json/shared/src/test/scala/zio/json/DecoderSpec.scala +++ b/zio-json/shared/src/test/scala/zio/json/DecoderSpec.scala @@ -16,13 +16,61 @@ object DecoderSpec extends ZIOSpecDefault { val spec: Spec[Environment, Any] = suite("Decoder")( suite("fromJson")( + test("byte") { + assert("-123".fromJson[Byte])(isRight(equalTo(-123: Byte))) && + assert("\"-123\"".fromJson[Byte])(isRight(equalTo(-123: Byte))) && + assertTrue("\"Infinity\"".fromJson[Byte].isLeft) && + assertTrue("\"-Infinity\"".fromJson[Byte].isLeft) && + assertTrue("\"NaN\"".fromJson[Byte].isLeft) + }, + test("short") { + assert("-12345".fromJson[Short])(isRight(equalTo(-12345: Short))) && + assert("\"-12345\"".fromJson[Short])(isRight(equalTo(-12345: Short))) && + assertTrue("\"Infinity\"".fromJson[Short].isLeft) && + assertTrue("\"-Infinity\"".fromJson[Short].isLeft) && + assertTrue("\"NaN\"".fromJson[Short].isLeft) + }, + test("int") { + assert("-1234567890".fromJson[Int])(isRight(equalTo(-1234567890))) && + assert("\"-1234567890\"".fromJson[Int])(isRight(equalTo(-1234567890))) && + assertTrue("\"Infinity\"".fromJson[Int].isLeft) && + assertTrue("\"-Infinity\"".fromJson[Int].isLeft) && + assertTrue("\"NaN\"".fromJson[Int].isLeft) + }, + test("long") { + assert("-123456789012345678".fromJson[Long])(isRight(equalTo(-123456789012345678L))) && + assert("\"-123456789012345678\"".fromJson[Long])(isRight(equalTo(-123456789012345678L))) && + assertTrue("\"Infinity\"".fromJson[Long].isLeft) && + assertTrue("\"-Infinity\"".fromJson[Long].isLeft) && + assertTrue("\"NaN\"".fromJson[Long].isLeft) + }, + test("float") { + assert("-1.234567e9".fromJson[Float])(isRight(equalTo(-1.234567e9f))) && + assert("\"-1.234567e9\"".fromJson[Float])(isRight(equalTo(-1.234567e9f))) && + assert("\"Infinity\"".fromJson[Float])(isRight(equalTo(Float.PositiveInfinity))) && + assert("\"-Infinity\"".fromJson[Float])(isRight(equalTo(Float.NegativeInfinity))) && + assertTrue("\"NaN\"".fromJson[Float].isRight) + }, + test("double") { + assert("-1.23456789012345e9".fromJson[Double])(isRight(equalTo(-1.23456789012345e9))) && + assert("\"-1.23456789012345e9\"".fromJson[Double])(isRight(equalTo(-1.23456789012345e9))) && + assert("\"Infinity\"".fromJson[Double])(isRight(equalTo(Double.PositiveInfinity))) && + assert("\"-Infinity\"".fromJson[Double])(isRight(equalTo(Double.NegativeInfinity))) && + assertTrue("\"NaN\"".fromJson[Double].isRight) + }, test("BigDecimal") { - assert("123".fromJson[BigDecimal])(isRight(equalTo(BigDecimal(123)))) + assert("123.0e123".fromJson[BigDecimal])(isRight(equalTo(BigDecimal("123.0e123")))) && + assertTrue("\"Infinity\"".fromJson[BigDecimal].isLeft) && + assertTrue("\"-Infinity\"".fromJson[BigDecimal].isLeft) && + assertTrue("\"NaN\"".fromJson[BigDecimal].isLeft) }, - test("256 bit BigInteger") { - assert("170141183460469231731687303715884105728".fromJson[java.math.BigInteger])( + test("BigInteger") { + assert("170141183460469231731687303715884105728".fromJson[BigInteger])( isRight(equalTo(new BigInteger("170141183460469231731687303715884105728"))) - ) + ) && + assertTrue("\"Infinity\"".fromJson[BigInteger].isLeft) && + assertTrue("\"-Infinity\"".fromJson[BigInteger].isLeft) && + assertTrue("\"NaN\"".fromJson[BigInteger].isLeft) }, test("BigInteger too large") { // this big integer consumes more than 256 bits @@ -56,7 +104,7 @@ object DecoderSpec extends ZIOSpecDefault { }, test("tuples") { assert("""["a",3]""".fromJson[(String, Int)])(isRight(equalTo(("a", 3)))) - assert("""["a","b"]""".fromJson[(String, Int)])(isLeft(equalTo("[1](expected a number, got 'b')"))) + assert("""["a","b"]""".fromJson[(String, Int)])(isLeft(equalTo("[1](expected an Int)"))) assert("""[[0.1,0.2],[0.3,0.4],[-0.3,-]]""".fromJson[Seq[(Double, Double)]])( isLeft(equalTo("[2][1](expected a Double)")) )