Context-aware JSON Format materialization macro
for Play framework
Note: This version supports Play framework 2.6.x with JDK 8.
For previous versions see older releases.
All frameworks including Play's macro inception consider only direct 1:1 mapping between classes and JSON. That means it accepts an instance and serializes it into JSON with all fields, with these names, in this format and type. However, there are many cases when we want some slight modification of the model before its serialization or after its reading. For example, we might want to rename a field, ignore a field, transform the value, e.g., a string to upper case or parse int to string, or consider the value only when a condition is satisfied. Furthermore, some of these, e.g., conditions, might depend on the current execution context including user's identity, application state, and the processed instance. For example, let's image that we write an instance of
User. His password should be ignored, and his email should be always lower-cased and visible only to him or an administrator. Now imagine how would you do this with common tools and frameworks, which usually do not support such complex behavior. You would have to either write your own
Writes instance or create alternatives of
User fitting desired situations.
This library fills this gap. It provides set of annotations and two complex macros reading them and generating context-aware
Writes seamlessly working with the Play framework. You have plenty of options how to customize your data during reading and writing without writing any boilerplate code. This gives you real power as everything is done during compilation, keeps the JSON protocol up-to-date with your models, and gives you powerful customization tools into your toolkit.
What is context-aware, why we want it
Writes instances provided by Play are stateless. They do not accept anything but the instance/JSON they process. Unfortunately, this is very limiting as sometimes we have to make a decision based on current context. For example, an email in
Users instance is visible only to him or admins, but we are unable to implement such
Writes as they do not consider the context. Furthermore, the context might contains other data such as current application state, user's preferences, geographical location, etc.
To overcome this gap the library silently brings
ContextualWrites accepting besides the instance/JSON also a
Context is an abstract type and it is up to you to implement it. That means it may contain anything you need available during processing. Having both
ContextualWrites and the
Context instance, it provides one-time-use
Writes instance matching the current context. The library simply makes instances of them using closures, there is not hidden magic. The significant benefit lies in silent introduction of a context and seamless cooperation with Play's API and inner implementation.
In conclusion, the library is context-aware as it considers current execution context and enables you to use it during your JSON reading/writing to modify the output.
Just as well as Play framework, this library uses macro inception instead of runtime inspection, reflection and complex output construction. Instead, it uses compile-time code generation (materialization macro) and it is fully type-safe, as it produces regular Scala code. There is no bytecode enhancement.
How to add the module into the project
Add the following lines into your SBT settings (
// context-aware json materializers libraryDependencies += "com.github.karelcemus" %% "play-json" % "1.1.0" // macro annotations require macro paradise in both the library and the project using it addCompilerPlugin( "org.scalamacros" % "paradise" % "2.1.1" cross CrossVersion.full )
This library requires JDK 8 or newer, Scala 2.11 or newer, and is designed for Play 2.6. However, it should work also with older releases of the Play framework as the JSON
Writes API has not changed for several releases. Furthermore, it uses advanced Scala macros, so it depends on Scala Macro Paradise. Add it into your project.
How to use this module
The module is automatically enabled by adding the macro paradise dependency into your project. The module is annotation-driven, i.e., to generate the
Writes just annotate classes.
JSON Protocol as
Core object of the module is the
Protocol. This object is implemented by the user as it declares type of
Context and optionally adds additional configuration.
@protocol.in, @protocol.out, @protocol.io
class, case class, materialization macro