4
votes

I have some case classes that extend this class:

class CitySuggestion(val name: String, val locationId: String, val locationKind: String)

I wanted to use Json.writes[CitySuggestion] as a JSON writer for this class, so I defined an unapply method in its companion object:

object CitySuggestion {
  def unapply(cs: CitySuggestion): Option[(String, String, String)] =
    Some((cs.name, cs.locationId, cs.locationKind))
}

I thought this would suffice, but the compiler now asks for an apply method as well:

No apply function found
[error] implicit lazy val citySuggestionWrites = Json.writes[CitySuggestion]

Why does it need an apply method? I don't want to deserialize the JSON, and I would have to add logic to figure out which subclass's instance it should be deserialized to.

1
If you make CitySuggestion a case class you won't need to manually define either method, though your use case might prevent that.gregghz
@greggory.hz thanks, I can't make it a case class because I'm extending that classZoltán

1 Answers

2
votes

This occurs because Play Json's writes & reads macros share a common implementation. Within this implementation the type A provided to Json.writes[A] is inspected for it's apply method, which in turn is used to generate the apply for the Writes instance.

As an alternative to using the 'writes' macro, you can create just roll your own Write[CitySuggestion] like this:

import play.api.libs.json._
import play.api.libs.functional.syntax._

implicit val citySuggestionWrites: Writes[CitySuggestion] = (
  (JsPath \ "name").write[String] and
  (JsPath \ "locationId").write[String] and
  (JsPath \ "locationKind").write[String]
)(unlift(CitySuggestion.unapply))