Use JSON with Play and Scala
Once getting your hands into typing scala code using play, the first thing that comes to mind is JSON. Without doubt JSON is one of the most basic components of web applications. Rest apis use json, your angular app has to consume json and the list goes on.
If you are lazy like me, you expect that it is sufficient to just pass back scala objects through your controller or specify a scala class as an argument to your controller. Somehow things don’t get far from that however some adjustments have to be done.
The first step is to specify the json module
libraryDependencies += json
The JSON library is pretty similar to the org.json library for java but with extra capabilities. The types we have out of the box are
JsString
JsNumber
JsBoolean
JsObject
JsArray
JsNull
However the key functionality comes from the Reads and Writes converters which can be used to marshal or unmarshal our data structures.
Suppose we have a class called User
case class User(id:Option[Long],email:String,firstName:String,lastName:String)
We want to use this class to pass data to our controllers or use it as a response, once our action has finished.
Thus we need to create a Reader and writer for the User object.
implicit val userWrites = new Writes[User] { def writes(user: User) = Json.obj( "id" -> user.id, "email" -> user.email, "firstName" -> user.firstName, "lastName" -> user.lastName ) } implicit val userReads: Reads[User] = ( (__ \ "id").readNullable[Long] and (__ \ "email").read[String] and (__ \ "firstName").read[String] and (__ \ "lastName").read[String] )(User.apply _)
Most probably you’ve noticed that the id is optional. We do so in order to be able to either pass the id of the user or not.
Now let’s put them together in a controller.
package controllers import javax.inject.Inject import com.google.inject.Singleton import play.api.libs.json._ import play.api.mvc.{Action, Controller} import play.api.libs.functional.syntax._ /** * Created by gkatzioura on 4/26/17. */ case class User(id:Option[Long],email:String,firstName:String,lastName:String) @Singleton class UserController @Inject() extends Controller { def all = Action { implicit request => val users = Seq( User(Option(1L),"gkazoura@example.com","Emmanouil","Gkatziouras"), User(Option(2L),"john@doe.com","John","Doe"), User(Option(3L),"john2@doe.com","John2","Doe2") ) Ok(Json.toJson(users)) } def greet = Action def add = Action { implicit request => val user = Json.fromJson[User](request.body.asJson.get).get val newUser = User(Option(4L),user.email,user.firstName,user.lastName) Ok(Json.toJson(newUser)) } implicit val userWrites = new Writes[User] { def writes(user: User) = Json.obj( "id" -> user.id, "email" -> user.email, "firstName" -> user.firstName, "lastName" -> user.lastName ) } implicit val userReads: Reads[User] = ( (__ \ "id").readNullable[Long] and (__ \ "email").read[String] and (__ \ "firstName").read[String] and (__ \ "lastName").read[String] )(User.apply _) }
And also the roots configuration
GET /user/ controllers.UserController.all POST /user/ controllers.UserController.add
As we can see the all method returns a list of user objects in Json format while the add method is supposed to persist a user object and assign an id to it.
Let’s do a curl request and check our results
curl http://localhost:9000/user/ .... [{"id":1,"email":"gkazoura@example.com","firstName":"Emmanouil","lastName":"Gkatziouras"},{"id":2,"email":"john@doe.com","firstName":"John","lastName":"Doe"},{"id":3,"email":"john2@doe.com","firstName":"John2","lastName":"Doe2"}] curl -H "Content-Type: application/json" -X POST -d '{"email":"emmanouil@egkatzioura.com","firstName":"Emmanouil","lastName":"Gkatziouras"}' http://localhost:9000/user/ ... {"id":4,"email":"emmanouil@egkatzioura.com","firstName":"Emmanouil","lastName":"Gkatziouras"}
So we didn’t get into any special json handling or reading instead we used only objects. That’s it! Now your are ready for more JSON related action!
You can check the sourcecode on github.
Reference: | Use JSON with Play and Scala from our JCG partner Emmanouil Gkatziouras at the gkatzioura blog. |