在 JSON 格式化程序中将 Option[String] 映射到 Option[Int]

Map Option[String] to Option[Int] in JSON formatter

这是我的用例:

外部JSON

..
lat: "xx.xx",
lng: "xx.xx",
..

我的(工作)reader,原样:

..
(__ \ 'lat).read[Option[String]] ~
(__ \ 'lng).read[Option[String]] ~
..

我只想将 String 映射到 Int。因为,从逻辑上讲,纬度和经度坐标应该这样表示。

这是我尝试过的方法,但没有用:

(__ \ 'lat).read[Option[String]].map(_.map(_.toInt) orElse None)) ~

我的案例 class 尝试执行上述操作时,否则它们 Option[String] 并且工作:

...
lat: Option[Int],
lng: Option[Int],
...

我认为问题可能只是语法上的问题,但是与括号的任何其他组合都无济于事。

编辑:

它编译,但是 JSON 不解析。它根本不构建我的对象。

如果我试试这个:

(__ \ 'lat).read[Option[String]].map(_.toInt)

我收到一个错误:

value toInt is not a member of Option[String]

但是在 REPL 中这是可行的:

val stringOpt: Option[String] = Some("10")
stringOpt.map(_.toInt)
res0: Option[Int] = Some(10)

调试 Reads 时,查看 validate[T] 可能发生的验证错误和异常很有帮助。您尚未发布案例 class,但反序列化格式为 xx.xxInt 的字段没有任何意义。调用 "20.20".toInt 时你会得到一个 NumberFormatExceptionDouble 可能更有意义,或者 BigDecimal.

这个有效:

case class Location(lat: Option[Double], lon: Option[Double])

implicit val reads: Reads[Location] = (
    (__ \ "lat").readNullable[String].map(_.map(_.toDouble)) and 
    (__ \ "lon").readNullable[String].map(_.map(_.toDouble))
)(Location.apply _)

当然,如果latlon不是数字,这会抛出异常。

处理此问题的最佳方法是根本不要将 JSON 中的数字格式化为字符串。然后 play-json 库将为您处理错误。如果这不可能,您可以考虑使用 Try.

(__ \ "lat").readNullable[String].map(_.flatMap(x => Try(x.toDouble).map(Some(_)).getOrElse(None)))