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:
Here we use the macro-based Json.format to create a reader and writer for the Person class:
But in our codebase things are a bit more complicated. We like to have type-safe IDs, and might use a Name wrapper for the first and last name, so our person might actually look like this:
In that case we have a helper to generate our ID serializer for each ID type, and generate the Name serializer using Json.format
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:
We can also define default values for properties we added. Let’s say I just added the email property:
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.
For more comprehensive information, you might find Play’s documentation on JSON combinators and handling JSON requests to be helpful.
We wrote this post while working on Kifi — Connecting people with knowledge. Learn more.
Originally published at eng.kifi.com on March 26, 2013.