diff --git a/play-json/jvm/src/main/scala/play/api/libs/json/EnvReads.scala b/play-json/jvm/src/main/scala/play/api/libs/json/EnvReads.scala index 30f3a7ffc..5a239d212 100644 --- a/play-json/jvm/src/main/scala/play/api/libs/json/EnvReads.scala +++ b/play-json/jvm/src/main/scala/play/api/libs/json/EnvReads.scala @@ -51,7 +51,7 @@ trait EnvReads { def dateReads(pattern: String, corrector: String => String = identity): Reads[java.util.Date] = new Reads[java.util.Date] { def reads(json: JsValue): JsResult[java.util.Date] = json match { - case JsNumber(d) => JsSuccess(new java.util.Date(d.toLong)) + case n: JsNumber => n.validate[Long].map(l => new java.util.Date(l)) case JsString(s) => parseJDate(pattern, corrector(s)) match { case Some(d) => JsSuccess(d) case None => JsError(Seq(JsPath -> @@ -95,7 +95,7 @@ trait EnvReads { val WithTz = """^[0-9]{4}-[0-9]{2}-[0-9]{2}T[0-9]{2}:[0-9]{2}:[0-9]{2}[^.]+$""".r def reads(json: JsValue): JsResult[Date] = json match { - case JsNumber(d) => JsSuccess(new Date(d.toLong)) + case n: JsNumber => n.validate[Long].map(l => new Date(l)) case JsString(s) => (s match { case WithMillisAndTz() => millisAndTz -> parseJDate(millisAndTz, s) @@ -227,7 +227,7 @@ trait EnvReads { epoch: Long => B ) extends Reads[B] { def reads(json: JsValue): JsResult[B] = json match { - case JsNumber(d) => JsSuccess(epoch(d.toLong)) + case n: JsNumber => n.validate[Long].map(epoch) case JsString(s) => p(parsing).parse(corrector(s)) match { case Some(d) => JsSuccess(d) case None => JsError(Seq(JsPath -> @@ -354,7 +354,7 @@ trait EnvReads { def localDateReads[T](parsing: T, corrector: String => String = identity)(implicit p: T => TemporalParser[LocalDate]): Reads[LocalDate] = new Reads[LocalDate] { def reads(json: JsValue): JsResult[LocalDate] = json match { - case JsNumber(d) => JsSuccess(epoch(d.toLong)) + case n: JsNumber => n.validate[Long].map(epoch) case JsString(s) => p(parsing).parse(corrector(s)) match { case Some(d) => JsSuccess(d) case _ => JsError(Seq(JsPath -> @@ -422,7 +422,7 @@ trait EnvReads { def localTimeReads[T](parsing: T, corrector: String => String = identity)(implicit p: T => TemporalParser[LocalTime]): Reads[LocalTime] = new Reads[LocalTime] { def reads(json: JsValue): JsResult[LocalTime] = json match { - case JsNumber(d) => JsSuccess(epoch(d.toLong)) + case n: JsNumber => n.validate[Long].map(epoch) case JsString(s) => p(parsing).parse(corrector(s)) match { case Some(d) => JsSuccess(d) case _ => JsError(Seq(JsPath -> @@ -514,11 +514,8 @@ trait EnvReads { private def jdurationNumberReads(unit: TemporalUnit) = Reads[JDuration] { - case JsNumber(n) if !n.ulp.isValidLong => - JsError("error.invalid.longDuration") - - case JsNumber(n) => JsSuccess(JDuration.of(n.toLong, unit)) - case _ => JsError("error.expected.lonDuration") + case n: JsNumber => n.validate[Long].map(l => JDuration.of(l, unit)) + case _ => JsError("error.expected.longDuration") } /** @@ -593,7 +590,7 @@ trait EnvReads { val df = DateTimeFormat.forPattern(pattern) def reads(json: JsValue): JsResult[DateTime] = json match { - case JsNumber(d) => JsSuccess(new DateTime(d.toLong)) + case n: JsNumber => n.validate[Long].map(l => new DateTime(l)) case JsString(s) => parseDate(corrector(s)) match { case Some(d) => JsSuccess(d) case _ => JsError(Seq(JsPath() -> Seq(JsonValidationError("error.expected.jodadate.format", pattern)))) @@ -635,7 +632,7 @@ trait EnvReads { val df = if (pattern == "") ISODateTimeFormat.localTimeParser else DateTimeFormat.forPattern(pattern) def reads(json: JsValue): JsResult[LocalTime] = json match { - case JsNumber(n) => JsSuccess(new LocalTime(n.toLong)) + case n: JsNumber => n.validate[Long].map(l => new LocalTime(l)) case JsString(s) => parseTime(corrector(s)) match { case Some(d) => JsSuccess(d) case None => JsError(Seq(JsPath() -> Seq(JsonValidationError("error.expected.jodatime.format", pattern)))) diff --git a/play-json/jvm/src/test/scala/play/api/libs/json/ReadsSpec.scala b/play-json/jvm/src/test/scala/play/api/libs/json/ReadsSpec.scala index 1d3ca6b79..30577b496 100644 --- a/play-json/jvm/src/test/scala/play/api/libs/json/ReadsSpec.scala +++ b/play-json/jvm/src/test/scala/play/api/libs/json/ReadsSpec.scala @@ -57,6 +57,12 @@ class ReadsSpec extends org.specs2.mutable.Specification { )) } + "not be read from invalid number" in { + reads(JsNumber(BigDecimal("1000000000e1000000000"))) aka "read date" must beLike { + case JsError((_, JsonValidationError("error.expected.long" :: Nil) :: Nil) :: Nil) => ok + } + } + "not be read from invalid string" in { reads(JsString("invalid")) aka "read date" must beLike { case JsError((_, JsonValidationError( @@ -224,6 +230,12 @@ class ReadsSpec extends org.specs2.mutable.Specification { } } + "not be read from invalid number" in { + reads(JsNumber(BigDecimal("1000000000e1000000000"))) aka "read date" must beLike { + case JsError((_, JsonValidationError("error.expected.long" :: Nil) :: Nil) :: Nil) => ok + } + } + "be successfully read with default implicit" >> { "from '2011-12-03T10:15:30+01:00' (with TZ offset)" in { reads(JsString("2011-12-03T10:15:30+01:00")) aka "read date" must_== ( @@ -299,6 +311,12 @@ class ReadsSpec extends org.specs2.mutable.Specification { aka("read date") must_== JsSuccess(d) } + "not be read from invalid number" in { + reads(JsNumber(BigDecimal("1000000000e1000000000"))) aka "read date" must beLike { + case JsError((_, JsonValidationError("error.expected.long" :: Nil) :: Nil) :: Nil) => ok + } + } + "not be read from invalid string" in { reads(JsString("invalid")) aka "read date" must beLike { case JsError((_, JsonValidationError( @@ -368,6 +386,12 @@ class ReadsSpec extends org.specs2.mutable.Specification { aka("read time") must_== JsSuccess(d) } + "not be read from invalid number" in { + reads(JsNumber(BigDecimal("1000000000e1000000000"))) aka "read date" must beLike { + case JsError((_, JsonValidationError("error.expected.long" :: Nil) :: Nil) :: Nil) => ok + } + } + "not be read from invalid string" in { reads(JsString("invalid")) aka "read time" must beLike { case JsError((_, JsonValidationError( @@ -432,6 +456,12 @@ class ReadsSpec extends org.specs2.mutable.Specification { aka("read date") must_== JsSuccess(Instant ofEpochMilli 123L) } + "not be read from invalid number" in { + reads(JsNumber(BigDecimal("1000000000e1000000000"))) aka "read date" must beLike { + case JsError((_, JsonValidationError("error.expected.long" :: Nil) :: Nil) :: Nil) => ok + } + } + "not be read from invalid string" in { reads(JsString("invalid")) aka "read date" must beLike { case JsError((_, JsonValidationError( @@ -575,7 +605,7 @@ class ReadsSpec extends org.specs2.mutable.Specification { JsString("1 seconds") -> JsError("error.invalid.duration"), JsString("foo") -> JsError("error.invalid.duration"), JsNumber(BigDecimal(1000L)) -> JsSuccess(oneSec), - JsNumber(BigDecimal(1.234D)) -> JsError("error.invalid.longDuration") + JsNumber(BigDecimal(1.234D)) -> JsError("error.expected.long") )) { case (input, result) => s"be parsed from ${Json stringify input} as $result" in { diff --git a/play-json/shared/src/test/scala/play/api/libs/json/JsonSharedSpec.scala b/play-json/shared/src/test/scala/play/api/libs/json/JsonSharedSpec.scala index bda04c3b1..fd86bbde8 100644 --- a/play-json/shared/src/test/scala/play/api/libs/json/JsonSharedSpec.scala +++ b/play-json/shared/src/test/scala/play/api/libs/json/JsonSharedSpec.scala @@ -270,7 +270,7 @@ class JsonSharedSpec extends WordSpec "key3" -> js.arr(1, "tutu") ) - js.prettyPrint(jo) mustEqual ("""{ + js.prettyPrint(jo).replaceAllLiterally("\r\n", "\n") mustEqual ("""{ "key1" : "toto", "key2" : { "key21" : "tata",