§JSON automated mapping
If the JSON maps directly to a class, we provide a handy macro so that you don’t have to write the Reads[T]
, Writes[T]
, or Format[T]
manually. Given the following case class :
case class Resident(name: String, age: Int, role: Option[String])
The following macro will create a Reads[Resident]
based on its structure and the name of its fields :
import play.api.libs.json._
implicit val residentReads = Json.reads[Resident]
When compiling, the macro will inspect the given class and
inject the following code, exactly as if you had written it manually :
import play.api.libs.json._
import play.api.libs.functional.syntax._
implicit val residentReads = (
(__ \ "name").read[String] and
(__ \ "age").read[Int] and
(__ \ "role").readNullable[String]
)(Resident)
This is done at compile-time, so you don’t lose any type safety or performance.
Similar macros exists for a Writes[T]
or a Format[T]
:
import play.api.libs.json._
implicit val residentWrites = Json.writes[Resident]
import play.api.libs.json._
implicit val residentFormat = Json.format[Resident]
So, a complete example of performing automated conversion of a case class to JSON is as follows:
import play.api.libs.json._
implicit val residentWrites = Json.writes[Resident]
val resident = Resident(name = "Fiver", age = 4, role = None)
val residentJson: JsValue = Json.toJson(resident)
And a complete example of automatically parsing JSON to a case class is:
import play.api.libs.json._
implicit val residentReads = Json.reads[Resident]
// In a request, a JsValue is likely to come from `request.body.asJson`
// or just `request.body` if using the `Action(parse.json)` body parser
val jsonString: JsValue = Json.parse(
"""{
"name" : "Fiver",
"age" : 4
}"""
)
val residentFromJson: JsResult[Resident] = Json.fromJson[Resident](jsonString)
residentFromJson match {
case JsSuccess(r: Resident, path: JsPath) => println("Name: " + r.name)
case e: JsError => println("Errors: " + JsError.toJson(e).toString())
}
Note: To be able to access JSON from request.body.asJson
, the request must have a Content-Type
header of application/json
. You can relax this constraint by using the tolerantJson body parser.
The above example can be made even more concise by using body parsers with a typed validation function. See the savePlaceConcise example in the JSON with HTTP documentation.
§Requirements
These macros rely on a few assumptions about the type they’re working with :
- It must have a companion object having apply
and unapply
methods
- The return types of the unapply
must match the argument types of the apply
method.
- The parameter names of the apply
method must be the same as the property names desired in the JSON.
Case classes natively meet these requirements. For more custom classes or traits, you might
have to implement them.
§Custom Naming Strategies
To use a custom Naming Strategy you need to define a implicit JsonConfiguration
object and a JsonNaming
.
Two naming strategies are provided: the default one, using as-is the names of the class properties,
and the JsonNaming.SnakeCase
case one.
A strategy other than the default one can be used as following:
import play.api.libs.json._
implicit val config = JsonConfiguration(SnakeCase)
implicit val userReads: Reads[PlayUser] = Json.reads[PlayUser]
import play.api.libs.json._
implicit val config = JsonConfiguration(SnakeCase)
implicit val userWrites: OWrites[PlayUser] = Json.writes[PlayUser]
import play.api.libs.json._
implicit val config = JsonConfiguration(SnakeCase)
implicit val userFormat: OFormat[PlayUser] = Json.format[PlayUser]
§Implementing your own Naming Strategy
To implement your own Naming Strategy you just need to implement the JsonNaming
trait:
import play.api.libs.json._
object Lightbend extends JsonNaming {
override def apply(property: String): String = s"lightbend_$property"
}
implicit val config = JsonConfiguration(Lightbend)
implicit val customWrites: OFormat[PlayUser] = Json.format[PlayUser]
Next: JSON Transformers
Found an error in this documentation? The source code for this page can be found here. After reading the documentation guidelines, please feel free to contribute a pull request. Have questions or advice to share? Go to our community forums to start a conversation with the community.