Sorona Sorona - 18 days ago 4
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 :(


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]]

// locationWrites with (JsPath \ "id").writeNullable[Long]