I'm going crazy trying to parse this JSON structure in Play Framework 2.2:
val jsonStr = """{ personFirstName: "FirstName",
personLastName: "LastName"
positionLat: null,
positionLon: null }"""
I have 2 case classes:
case class Position( val lat: Double, val lon: Double)
case class Person( firstName: String, lastName: String, p: Option[Position] )
As you can see, Position is not mandatory in Person case class.
I was trying to get an instance of Person using something like this
implicit val reader = (
(__ \ 'personFirstName ).read[String] ~
(__ \ 'personLastName ).read[String] ~
( (__ \ 'positionLat ).read[Double] ~
(__ \ 'positionLon ).read[Double] )(Position)
)(Person)
but I soon realized I have no idea how to deal with the Option[Position]
object: the intention would be to instantiate a Some(Position(lat,lon))
if both 'lat' and 'lon' are specified and not null, otherwise instantiate None
.
How would you deal with that?
I'm pretty sure there's a better way of doing what you want than what I'm going to post, but it's late and I can't figure it out now. I'm assuming that simply changing the JSON structure you're consuming isn't an option here.
You can supply a builder function that takes two optional doubles for lat/lon and yields a position if they're both present.
import play.api.libs.functional.syntax._
import play.api.libs.json._
val jsonStr = """{
"personFirstName": "FirstName",
"personLastName": "LastName",
"positionLat": null,
"positionLon": null }"""
case class Position(lat: Double, lon: Double)
case class Person( firstName: String, lastName: String, p: Option[Position] )
object Person {
implicit val reader = (
(__ \ "personFirstName" ).read[String] and
(__ \ "personLastName" ).read[String] and (
(__ \ "positionLat" ).readNullable[Double] and
(__ \ "positionLon" ).readNullable[Double]
)((latOpt: Option[Double], lonOpt: Option[Double]) => {
for { lat <- latOpt ; lon <- lonOpt} yield Position(lat, lon)
})
)(Person.apply _)
}
Json.parse(jsonStr).validate[Person] // yields JsSuccess(Person(FirstName,LastName,None),)
Also, note that to be valid JSON you need to quote the data keys.
Your javascript object should match the structure of your case classes. Position
will need to have a json reader as well.
val jsonStr = """{ "personFirstName": "FirstName",
"personLastName": "LastName",
"position":{
"lat": null,
"lon": null
}
}"""
case class Person( firstName: String, lastName: String, p: Option[Position] )
object Person {
implicit val reader = (
(__ \ 'personFirstName ).read[String] ~
(__ \ 'personLastName ).read[String] ~
(__ \ 'position ).readNullable[Position]
)(Person.apply _)
}
case class Position( val lat: Double, val lon: Double)
object Position {
implicit val reader = (
(__ \ 'lat ).read[Double] ~
(__ \ 'lon ).read[Double]
)(Position.apply _)
}
If either of the fields of Position
are null/missing in the json object, it will be parsed as None
. So, jsonStr.as[Person] = Person("FirstName", "LastName", None)
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With