Reputation: 1086
Here's my use case:
External JSON
..
lat: "xx.xx",
lng: "xx.xx",
..
My (working) reader, as is:
..
(__ \ 'lat).read[Option[String]] ~
(__ \ 'lng).read[Option[String]] ~
..
All I would like to do is map the String
to an Int
. Since, logically, lat & long coords should be represented as such.
Here is what I tried, and doesn't work:
(__ \ 'lat).read[Option[String]].map(_.map(_.toInt) orElse None)) ~
My case class when trying to do the above, otherwise they're Option[String]
and work:
...
lat: Option[Int],
lng: Option[Int],
...
I think the problem might be simply syntactical, however any other combinations with parens doesn't help.
Edit:
It compiles, however the JSON doesn't parse. It simply doesn't build my objects.
If I try this:
(__ \ 'lat).read[Option[String]].map(_.toInt)
I get an error:
value toInt is not a member of Option[String]
However in the REPL this works:
val stringOpt: Option[String] = Some("10")
stringOpt.map(_.toInt)
res0: Option[Int] = Some(10)
Upvotes: 0
Views: 339
Reputation: 55569
When debugging Reads
, its helpful to look through the validation errors and exceptions that may occur from validate[T]
. You haven't posted your case class, but deserializing fields that are formatting as xx.xx
to Int
doesn't make any sense. You'll get a NumberFormatException
when calling "20.20".toInt
. Double
would probably make more sense, or BigDecimal
.
This works:
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 _)
Of course, if lat
or lon
is not a number, this will throw an exception.
The best way to handle this is to not format your numbers in JSON as strings at all. Then the play-json library will handle the errors for you. If this is not possible, you might consider using Try
.
(__ \ "lat").readNullable[String].map(_.flatMap(x => Try(x.toDouble).map(Some(_)).getOrElse(None)))
Upvotes: 4