Working with JSON in Play 2.1

Play 2.1 now provides a really nice library for formatting Scala objects as JSON. As we’ve migrated from Play 2.0 to Play 2.1, we’ve been taking advantage of several of its features to reduce the size and complexity of our code.

To demonstrate how we use it, let’s start with a simple example. Suppose I have a Person object that I want to both serialize and deserialize:

You can see that we need to be a bit more verbose when creating formatters for single-element case classes because of limitations in the Play API.

Using functional combinators

Sometimes we want the external JSON to be structured differently than our Scala code. Suppose we want to place the firstName and lastName properties directly in the JSON for a Person and want to validate the email address format. Then we can use the functional combinators to construct our Format[Person]:

Notice we used the provided Reads.email to validate the email (along with the implicit string writer). In practice, we might use an Email value class wrapper and write a serializer for that, but I’ll leave that as an exercise for the reader.

Backwards compatibility

If we’ve decided to store our JSON anywhere and then change the serializer, we might run into backwards-compatibility issues when reading back the old JSON. There are a couple of ways to deal with this problem. One is to keep your old reader around and define a new reader using orElse:

Note that we can use Format#inmap (or Reads#map/Writes#contramap) to do a transformation between the Option[String] we get from formatNullable and a String with a default value.

Formatting DateTimes

It’s also nice to have DateTime values printed as human-readable strings (as is the case for LocalDate shown in our example). We can easily define a formatter to do this. The format pattern used is the same as in SimpleDateFormat.

Conclusion

As you can see, Play 2.1 makes it really easy to read and write Scala case classes as JSON. We can create simple serializers using the macro-based approach, and deal with more complex cases using the functional combinators.