Sorona Sorona - 1 month ago 11
JSON Question

Json to case class with Option[Long]

Consider this case class

case class Location(val name: String, val id: Option[Long] = None)


I can write a "toJson" like so:

implicit val locationWrites: Writes[Location] = (
(JsPath \ "name").write[String] and
(JsPath \ "id").write[Option[Long]]
) (unlift(Location.unapply))


but I am struggling with a "fromJson":

implicit val locationReads: Reads[Location] = (
(JsPath \ "name").read[String]
) (Location.apply _, None)


Explanation: The id should actually be set by the persistence layer, so I don't want to provide it manually.

I also tried:

implicit val locationReads: Reads[Location] = (
(JsPath \ "name").read[String] and
(JsPath \ "id").read[Option[Long]]
) (Location.apply _)


but sadly then I get:


No Json deserializer found for type Option[Long]. Try to implement an implicit Reads or Format for this type.


Do I really need to create an implicit for that as well?

How come this works (as in: compiles)? I ask because it is absolutely trivial to humans, yet suddenly there is an implicit and it compiles. Not sure if this makes sense at all though, but I fear not

implicit val optionalLongReads: Reads[Option[Long]] = (
(JsPath \ "optional").read[Option[Long]]
)


edit: definitely makes no sense.

If I send
curl --include --request POST --header "Content-type:application/json" --data '{"name": "Gondor", "id": "2"}' localhost:9000
I get:


HTTP/1.1 500 Internal Server Error

Content-Length: 8029

Content-Type: text/html; charset=utf-8

Date: Sat, 08 Oct 2016 21:49:51 GMT


I cannot leave id empty either :(

Answer

Play Framework supports Option field out of the box.

You must use readNullable[Long] instead of read[Option[Long]].

implicit val locationReads: Reads[Location] = (
  (JsPath \ "name").read[String] and
  (JsPath \ "id").readNullable[Long]
)(Location.apply _)

You can also use writeNullable[Long] in your locationWrites.

With write[Option[Long]] resulting json will contains id field set to null in case id is None. With writeNullable[Long] id field won't be present at all for None case:

val location = Location("Gondor", None)

// locationWrites with (JsPath \ "id").write[Option[Long]]
{
   "name":"Gondor",
   "id":null
}

// locationWrites with (JsPath \ "id").writeNullable[Long]
{
   "name":"Gondor"
}
Comments