Play Framework: rendering custom JSON objects
I am using Play Framework 1.2.4 with Java and am using JPA to persist database objects. I have several model classes that will render as JSON. But the problem is, I would like to customize these JSON responses and simplify the objects just before rendering as JSON.
For example, suppose I have an object named ComplexClass and has properties id, name, property1, ..., propertyN. In the JSON response, I would like to display only the id and name fields.
What's the most elegant way to do this? Writing custom binder objects or simple JSON mapping like using a template?
source to share
Use FlexJSON, it's very easy. It allows you to create JSONSerializers that can include / exclude the fields you want.
Check out this article for some examples of using this in Play! Framework.
Here's a simple example:
public ComplexClass {
public Long id;
public String name;
// And lots of other fields you don't want
public String toJsonString() {
// Include id & name, exclude all others.
JSONSerializer ser = new JSONSerializer().include(
"id",
"name",
).exclude("*");
return ser.serialize(this);
}
}
You can add it to your .yml dependencies like this:
require:
- play
- net.sf.flexjson -> flexjson 2.1
I usually write an interface for the models that implement the method toJSONString()
so that I can call renderJSON(someModel.toJSONString())
in the controller.
EDIT : Additional example for lists / collections
Well, when you start serializing the list, you might get unexpected results. This is because the order of evaluation is important. The first include()
or exclude()
takes precedence over the next.
Here is an example of serializing the children of the parent object (OneToMany relationship).
JSONSerializer ser = new JSONSerializer();
// Exclude these standard fields from childs
ser.exclude(
"*.persistent",
"*.class",
"*.entityId"
);
// Include childs and all its other fields
ser.include(
"childs",
"childs.*"
);
// Exclude everything else
ser.exclude("*");
String data = ser.serialize(parent);
*
is a wildcard. This documentation explains it completely:
Exclude *.class
will match any path depth. Therefore, if flexjson serializes field loop "foo.bar.class", *
to *.class
be fit foo.bar.
source to share
The Play Framework 1.2.4 is directly dependent on the library gson
, so you can use it to render JSON strings. All you have to do is use the gson annotation @Expose
. So in your example, you tag the fields you want in the JSON string like this:
public class ComplexClass {
@Expose
public Long id;
@Expose
public String name;
...
}
Then, in your controller, you simply do this:
public static void someActionMethod() {
// get an instance of your ComplexClass here
ComplexClass complex = ...
Gson gson = new GsonBuilder().excludeFieldsWithoutExposeAnnotation().create()
String json = gson.toJson(complex);
renderJson(json);
}
See the documentation here .
If the ComplexClass is in fact play.db.jpa.Model
, and so the field is id
abstracted in the parent class and you cannot put annotation @Expose
on it, then you can create your own ExclusionStrategy that skips fields that are not annotated with @Expose
and named id
. So, something like this (pseudocode):
public final class ComplexClassExclusionStrategy implements ExclusionStrategy {
public boolean shouldSkipField(FieldAttributes attributes) {
if (name of field is "id") return false;
if (field is annotated with @Expose) return false;
return true;
}
Then the controller changed a bit to look like this:
GsonBuilder builder = new GsonBuilder();
ComplexClassExclusionStrategy strategy = new ComplexClassExclusionStrategy();
builder.setExclusionStrategies(strategy);
Gson gson = builder.create();
String json = gson.toJson(complex);
renderJson(json);
source to share