Проблема в том, что по умолчанию play-json конфигурирует синтаксический анализатор Джексона с MathContext
, установленным на DECIMAL128
. Это можно исправить, установив системное свойство play.json.parser.mathContext
на unlimited
. Например, в REPL Scala это будет выглядеть так:
scala> System.setProperty("play.json.parser.mathContext", "unlimited")
res0: String = null
scala> val j ="""{"t":2.2599999999999997868371792719699442386627197265625}"""
j: String = {"t":2.2599999999999997868371792719699442386627197265625}
scala> import play.api.libs.json.Json
import play.api.libs.json.Json
scala> val res = (Json.parse(j) \ "t").as[BigDecimal]
res: BigDecimal = 2.2599999999999997868371792719699442386627197265625
scala> val expected = BigDecimal("2.2599999999999997868371792719699442386627197265625")
expected: scala.math.BigDecimal = 2.2599999999999997868371792719699442386627197265625
scala> res.compare(expected)
res1: Int = 0
Обратите внимание, что setProperty
должно произойти в первую очередь, перед любой ссылкой на Json
. При обычном использовании (не REPL) вы устанавливаете свойство через -D
в командной строке или как угодно.
В качестве альтернативы вы можете использовать Jawn поддержку синтаксического анализа play-json, которая работает как положено с полки:
scala> val j ="""{"t":2.2599999999999997868371792719699442386627197265625}"""
j: String = {"t":2.2599999999999997868371792719699442386627197265625}
scala> import org.typelevel.jawn.support.play.Parser
import org.typelevel.jawn.support.play.Parser
scala> val res = (Parser.parseFromString(j).get \ "t").as[BigDecimal]
res: BigDecimal = 2.2599999999999997868371792719699442386627197265625
Или, если на то пошло, вы можете переключиться на circe :
scala> import io.circe.Decoder, io.circe.jawn.decode
import io.circe.Decoder
import io.circe.jawn.decode
scala> decode(j)(Decoder[BigDecimal].prepare(_.downField("t")))
res0: Either[io.circe.Error,BigDecimal] = Right(2.2599999999999997868371792719699442386627197265625)
… который, на мой взгляд, обрабатывает ряд связанных с числом угловых случаев более ответственно, чем play-json. Например:
scala> val big = "1e2147483648"
big: String = 1e2147483648
scala> io.circe.jawn.parse(big)
res0: Either[io.circe.ParsingFailure,io.circe.Json] = Right(1e2147483648)
scala> play.api.libs.json.Json.parse(big)
java.lang.NumberFormatException
at java.math.BigDecimal.<init>(BigDecimal.java:491)
at java.math.BigDecimal.<init>(BigDecimal.java:824)
at scala.math.BigDecimal$.apply(BigDecimal.scala:287)
at play.api.libs.json.jackson.JsValueDeserializer.parseBigDecimal(JacksonJson.scala:146)
...
Но это выходит за рамки этого вопроса.
Если честно, я не уверен, почему play-json по умолчанию DECIMAL128
для MathContext
, но это вопрос для сопровождающих play-json, и он также выходит за рамки этого.