Documentation

The Play JSON library Basics

Overview

The recommended way of dealing with JSON is using Play’s typeclass based JSON library, located at play.api.libs.json.

For parsing JSON strings, Play uses the super-fast Java based JSON library, Jackson.

The benefit of this approach is that both the Java and the Scala side of Play can share the same underlying library (Jackson), while Scala users can enjoy the extra type safety and functional aspects that Play’s JSON support brings to the table.

JSON is an AST (_Abstract Syntax Tree_)

Take this JSON for example:

{ 
  "user": {
    "name" : "toto",
    "age" : 25,
    "email" : "toto@jmail.com",
    "isAlive" : true,
    "friend" : {
  	  "name" : "tata",
  	  "age" : 20,
  	  "email" : "tata@coldmail.com"
    }
  } 
}

This can be seen as a tree structure using the two following structures:

If you want to have more info about the exact JSON standard, please go to json.org

Json Data Types

The play.api.libs.json package contains the seven JSON data types, reflecting this structure.

JsObject

This is a set of name/value pairs as described in the standard, for example:

{ "name" : "toto", "age" : 45 }

JsNull

This represents a null value in JSON.

JsBoolean

This is a boolean with a value of true or false.

JsNumber

JSON does NOT discriminate short, int, long, float, double and BigDecimal, so it is represented by a JsNumber containing a BigDecimal. The Play JSON API brings more type precision when converting to Scala structures.

JsArray

An array is a sequence of any Json value types (not necessarily the same type), for example:

[ "alpha", "beta", true, 123.44, 334]

JsString

A classic String.

Other data types

JsUndefined

This is not part of the JSON standard and is only used internally by the API to represent some error nodes in the AST.

JsValue

This is the super type of all the other types.

Working with JSON

The entry point into Play’s JSON API is play.api.libs.json.Json. It provides the following methods:

Parsing a JSON String

You can easily parse any JSON string as a JsValue:

import play.api.libs.json._

val json: JsValue = Json.parse("""
{
  "user": {
    "name" : "toto",
    "age" : 25,
    "email" : "toto@jmail.com",
    "isAlive" : true,
    "friend" : {
      "name" : "tata",
      "age" : 20,
      "email" : "tata@coldmail.com"
    }
  }
}
""")

The json value that the result was assigned to is used in subsequent code samples below.

Constructing JSON directly

Raw way

The previous sample Json object can be created in other ways too. Here is the raw approach:

import play.api.libs.json._

JsObject(Seq(
  "users" -> JsArray(Seq(
    JsObject(Seq(
      "name" -> JsString("Bob"),
      "age" -> JsNumber(31),
      "email" -> JsString("bob@gmail.com")
    )),
    JsObject(Seq(
      "name" -> JsString("Kiki"),
      "age" -> JsNumber(25),
      "email" -> JsNull
    ))
  ))
))

Preferred way

Play also provides a simplified syntax to build your JSON. The previous JsObject can be constructed using the following:

import play.api.libs.json._

Json.obj(
  "users" -> Json.arr(
    Json.obj(
      "name" -> "Bob",
      "age" -> 31,
      "email" -> "bob@gmail.com"
    ),
    Json.obj(
      "name" -> "Kiki",
      "age" -> 25,
      "email" -> JsNull
    )
  )
)

Serializing JSON

Serializing a JsValue to its JSON String representation is easy:

val jsonString: String = Json.stringify(json)

Accessing Path in a JSON tree

As soon as you have a JsValue you can navigate into the JSON tree. The API looks like the one provided to navigate into XML document by Scala using NodeSeq except you retrieve JsValue.

Simple path \

You can navigate through properties in an object using the \ method:

val name: JsValue = json \ "user" \ "name"

name === JsString("toto")

Recursive path \\

val emails: Seq[JsValue] = json \ "user" \\ "email"

emails === Seq(JsString("toto@jmail.com"), JsString("tata@coldmail.com"))

Converting JsValue to Scala Value

While navigating JSON tree, you retrieve JsValue, however you may want to convert the JsValue to a Scala type. For example, you may want a JsString to be converted to a String, or a JsNumber to be converted to a Long.

Unsafe conversion with json.as[T]

The simplest way to convert it to a value is to use the as[T] method, like so:

val name: String = (json \ "user" \ "name").as[String]

name === "toto"

This method however is unsafe, if the path is not found, or the conversion is not possible, a JsResultException is thrown, containing the error.

Please note the error that doesn’t return path.not.found as you may expect. This is a difference from JSON combinators presented later in the doc.
This is due to the fact that (json \ "user" \ "nameXXX") returns JsNull and the implicit Reads[String] here awaits a JsString which explains the detected error.

Safer conversion with Option[T]

The asOpt[T] method is like as[T], however it will return None instead of throwing an exception if the path isn’t found, or the conversion isn’t possible:

val maybeName: Option[String] = (json \ "user" \ "name").asOpt[String]

maybeName === Some("toto")

Safest conversion with validate[T]

asOpt[T] is better but you lose the kind of error that was detected.

validate[T] is there to provide the safest and most robust way to convert a JsValue by returning a JsResult[T]:

JsResult[T] in a nutshell

JsResult[T] can have 2 values:

Please note : JsPath will be described later but it is just the same as XMLPath for JSON.
When you write :
json \ "user" \ "name"
It can be written as following :
(JsPath \ "user" \ "name")(json)
You create a JsPath to search user then name and apply it to a given json.

So a successful conversion might look like this:

val jsresult: JsResult[String] = (json \ "user" \ "name").validate[String]

jsresult === JsSuccess("toto")

If however the path wasn’t found, we get this:

val nameXXX = (json \ "user" \ "nameXXX").validate[String]

nameXXX === JsError(List((JsPath, List(ValidationError("error.expected.jsstring")))))

Since map and flatMap are provided, for comprehensions can easily be used to extract values out:

val nameAndEmail: JsResult[(String, String)] = for {
  name <- (json \ "user" \ "name").validate[String]
  email <- (json \ "user" \ "email").validate[String]
} yield (name, email)

nameAndEmail must_== JsSuccess(("toto", "toto@jmail.com"))

Converting Recursive path \\

\\ recursively searches in the sub-tree and returns a Seq[JsValue] of found JsValue which is then a collection with classical Scala functions.

val emails: Seq[String] = (json \ "user" \\ "email").map(_.as[String])

emails === Seq("toto@jmail.com", "tata@coldmail.com")

Converting a Scala value to JsValue

Scala to JSON conversion is performed by function Json.toJson[T](implicit writes: Writes[T]) based on implicit typeclass Writes[T] which is just able to convert a T to a JsValue.

Create very simple JsValue

import play.api.libs.json._

val jsonNumber = Json.toJson(4)

jsonNumber === JsNumber(4)

This conversion is possible because Play JSON API provides an implicit Writes[Int]

Create a JSON array from a Seq[T]

import play.api.libs.json._

val jsonArray = Json.toJson(Seq(1, 2, 3, 4))

jsonArray === Json.arr(1, 2, 3, 4)

This conversion is possible because Play JSON API provides an implicit Writes[Seq[Int]]

Here we have no problem to convert a Seq[Int] into a Json array. However it is more complicated if the Seq contains heterogeneous values:

import play.api.libs.json._

val jsonArray = Json.toJson(Seq(1, "Bob", 3, 4))

When we try to compile this, we get the following error:

No Json deserializer found for type Seq[Any]. Try to implement an implicit Writes or Format for this type.

This is because there is no way to convert a Seq[Any] to Json (Any could be anything including something not supported by Json right?)

A simple solution is to handle it as a Seq[JsValue]:

import play.api.libs.json._

val jsonArray = Json.toJson(Seq(
  Json.toJson(1), Json.toJson("Bob"), Json.toJson(3), Json.toJson(4)
))

jsonArray === Json.arr(1, "Bob", 3, 4)

This conversion is possible because Play API JSON provides an implicit Writes[Seq[JsValue]]

Next: JSON Reads/Writes/Formats Combinators