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:

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:

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

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:

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:

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:

Let’s create an implicit class to extend JsObject:

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]:

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:

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).

Recent posts

Tag cloud

Akka BeanPurée DSL Gentoo Hakyll Haskell Java NVidia Scala Spray XMonad collections git sbt sbt-git-flow-version shapeless