Easy JSON analyze with spray-json

Posted on April 30, 2016

If you use Spray or Akka HTTP to create a REST service, possibly you need to work with JSON objects. Both Spray and Akka HTTP have built-in support of spray-json.

In most cases you have a fixed set of fields in your JSON API, so the proposed way to work with spray-json is to create model case classes and marshallers/unmarshallers for them:

case class Person(name: String, age: Int, title: Option[String])

trait PersonRoutes extends DefaultJsonProtocol with SprayJsonSupport {
  implicit val personFormat = jsonFormat3(Person)

  val route =
    put {
      path("people") {
        entity(as[Person]) { person =>
          // Store to db

In this example the implicit formatter does all JSON validation and deserialization work. So person is an instance of the class Person. But sometimes you might not have a strict model, but just some JSON object. You can represent this data as an instance of JsObject class which is defined as:

case class JsObject(fields: Map[String, JsValue]) extends JsValue

So, if we’d like to add some schemeless information to our Person class we can just add a JsObject field.

case class Person(name: String, age: Int, title: Option[String], extras: JsObject)

The problem is that this is not really useful to inspect JsObject. The only thing you can do with it is to get its fields like a Map[String, JsValue].

For example some of the Person objects have address information:

    "name" : "John Doe",
    "age" : 42,
    "extras" : {
        "address" : {
            "city"  : "Moscow",
            "street" : "Zemlyanoy Val"

Let’s say we’d like to inspect these objects and pass only if the city is Moscow. To do that we can write something like:

entity(as[Person]) { person =>
  val city = for {
    addr <- person.extras.fields.get("address")
    ao   <- Try(addr.asJsObject).toOption
    c    <- ao.fields.get("city")
  } yield c

  if (city.map(_ == "Moscow").getOrElse(false)) {
  } else {

This code looks quite ugly. Even with two layers we have a lot of boilerplate steps: extract a JsValue, convert it to a JsObject, extract the next value, etc. It would be nice to have DSL for traversing through the objects. I think it can be similar to XPath:

entity(as[Person]) { person =>
  if (person / "address" / "city" === "Moscow") {
  } else {

Let’s create an implicit class to extend JsObject:

implicit class JsObjectOps(val o: JsObject) extends AnyVal {
  def / (name: String) = ???

But what should it return? We can return a JsValue, but there are several problems:

  1. The object might not contain the field we are looking for. So, we need at least an Option[JsValue].
  2. We’d like to chain path elements to create more complex paths.
  3. We need to have === and =!= operators to check the returned values.

To meet all of these requirements, we need to create another class, which will wrap the Option[JsValue]:

class JsFieldOps(val field: Option[JsValue]) {
  def /(name: string) = field map (_ / name) getOrElse this
  def ===(x: JsValue) = field.contains(x)
  def =!=(x: JsValue) = !field.contains(x)

The implementation of === and =!= is quite obvious. We just check the values in the underlying field. The most interesting part is the / method (but don’t get too excited – this one is not rocket science either :)). There are two cases. If the field is empty we can just return the same empty object. But if not, we can apply the same / we used initially to create this object (that’s the part I left not implemented yet in the very beginning of the implementation). So it looks like we need to extend the JsValue, not the JsObject to add /, but it has to be applicable only to the objects. There is a method called asJsObject in the JsObject class, which throws an exception if the class is not a JsObject. Thus, the implementation of the JsValueOps (instead of the JsObjectOps) will be like:

import scala.util.Try
import spray.json.JsValue

implicit class JsValueOps(val value: JsValue) extends AnyVal {
  def /(name: String) =

And this is a complete implementation of simple DSL for querying values in JsObjects.

P.S. there is a great library called json-lenses which provides a more powerful way to query and update JSON objects. It gives you objects called “lens”, which encapsulate a path through a JSON object, and allows you to get and set values (of course set means create a modified object, because JsObject is immutable).