This is a study note for Play JSON and Play WS.

1 Play JSON

The play.api.libs.json package contains JSON-related data structures and utilities to convert JSON data. It supports automatic parsing and automatic conversion.

The JsValue trait represents a JSON value that could be one of JsString, JsNumber, JsBoolean, JsObject, JsArray and JsNull. The Json object provides utilities providing conversion to and from JsValue. The JsPath represent a path into a JsValue structure, like the XPath for XML.

Use Json.parse() to parse a string into a JsValue. Use JsObject(), Json.obj() or Json.arr to construct a JSON object.

1.1 Convert to JsValue

The Json.toJson[T](T)(implicit writes: Writes[T]) converts a Scala value to JsValue. Writes[T] converts a value of T to JsValue. The Play JSON API comes with implicit Writes for basic types such as Int, String, and Boolean ect. It also support for collections, such as Seq and List, of any type T that a Writes[T] exists.

1.2 Traverse a JsValue Structure

Use the \ operator to access a property of a JsValue or an indexed value in JsArray. The \ operator returs a JsLookupResult that is either a JsDefined or JsUndefined.

Use \\ operator to lookup for the field in the current object and all descendants.

1.3 Convert from a JsValue

Json.stringify() or Json.prettyPring() return a string from a JsValue.

Use JsValue.as[T](implicit fjs: Reads[T]): T to convert a JsValue to a value of type T. Reads[T] convert a JsValue to T. The JSON API provides Reads for basic types.

Use JsValue.asOpt to return an Option[T].

Use JsValue.validate to validate and convert from a JsValue. The result is a type of JsResult that could be JsSuccess or JsError. the JsResult has methods such as getOrElse, map, and fold.

To covert a JsValue to model, you need to define a Reads[T].

1.4 JSON with HTTP

To send JSON data, convert model data to JsValue using Json.toJson() method. For received data with text/json or application/json headers, Play uses a JSON BodyParser that generates a JsValue.

1.5 JSON Reads/Writes/Format Combinators

JsPath supports three path types:

  • simple path: `JsPath \ “location” \ “lat”
  • recursive path: JsPath \\ "name"
  • indexed path: (JsPath \ "residents")(0)

JsPath has JsPath.read[T](implicit r: Reads[T]): Reads[T] and JsPath.readNullable[T](implicit r: Reads[T]): Reads[Option[T]] methods to create special Reads. Use and, keepAnd and andKeep combinators to combine different parts and returns a FunctionalBuilder. Use the builder’s apply with a model’s apply _.

Similarly, the write[T] method uses the and combinator to create a builder and finally calls unlift(Model.unapply).

Use lazyRead and lazyWrite to handle recursive types.

Format[T] is a mix of the Read and Writes. It is used for implicit conversion in place of its components.

1.6 Auto Mapping

Json.reads[T], Json.writes[T] and Json.format[T] are macros that can be used to generate Reads[T], Writes[T] and Format[T]. It is done at compile-time.

Json.valueReads[T], Json.valueWrites[T], and Json.valueFormat[T] are used for value classes.

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.

2 Play WS

For compile time DI, use AhcWSComponents. Use an instance of WSClient to build a request with various HTTP options. Following is an example:

1
2
3
4
val request: WSRequest = ws.url(url)
  .addHttpHeaders("Accept" -> "application/json")
  .addQueryStringParameters("search" -> "play")
  .withRequestTimeout(10000.millis)

To add cookies, use addCookies method with values of DefaultWSCookie or play.api.mvc.Cookie.

The get() and post() method will send the request and return a Future[WSResponse]. To Post url-form-encoded data, use Map[String, Seq[String]] as body. If the body is empty, use play.api.libs.ws.EmptyBody.

Use the map method to process Future[WSResponse]. An implicit execution context must be available to process the result. The WSResponse extends play.api.libs.ws.WSBodyReadables trait that contains a type class for Play JSON conversion. For example, map { response => (response.json \ "person" \ "name").as[String]. For auto mapping, use validate[T] to create a Future[JsResult[T]].