Play!> & JSon : How-to select fields to expose (ExclusionStrategy)
Using JSON as data language to build an API for your Play application, the default behaviour is to render all fields of a class, and this recursively. This means render the fields of a
Here a sample model example that could occurs in a bank application :
A client is specified with this model, that includes the password of the client.
@Entity
public class Client extends Model {
public String firstname;
public String lastname;
public String password;
public String toString() {
return firstname + " " + lastname;
}
}
This client has accounts in the bank, so a simple model would be :
@Entity
public class Account extends Model {
@ManyToOne
public Client owner;
public String number;
public Double balance;
public String label;
public String toString() {
return number;
}
}
One of the first service of the application when a client connects to the application is to show the list of his accounts :
The route is :
GET /api/accounts Operations.listAccounts
And the code of the controller is :
public class Operations extends Controller {
public static void listAccounts() {
List<Account> accounts = Account.find("order by number").fetch();
renderJSON(accounts);
}
}
This code will expose the following json datas :
[\{"owner":\{"firstname":"xxx","lastname":"xxx","password":"yyyy","id":1},"number":"001","balance":999.99,"label":"ACCOUNT X","id":1},...]
Oh, the password is visible.
So what we will do is to extend Play! with a new Controller class that will use add a Strategy to gson :
package ext;
import play.mvc.Controller;
import play.mvc.Util;
import play.mvc.results.RenderJson;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
public abstract class NoExposeStrategyController extends Controller {
private static final Gson gson;
static {
gson = new GsonBuilder()
.addSerializationExclusionStrategy(new NoExposeExclusionStrategy())
.create();
}
@Util
public static void renderJSON(Object object) {
throw new RenderJson(gson.toJson(object));
}
}
Now, we will wrtie this strategy. The goal is to exclude fiels annoted (we will use the tag @NoExpose) in the json output.
package ext;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import com.google.gson.ExclusionStrategy;
import com.google.gson.FieldAttributes;
public class NoExposeExclusionStrategy implements ExclusionStrategy {
@Retention(RetentionPolicy.RUNTIME)
@Target({ ElementType.FIELD })
public @interface NoExpose {
// Field tag only annotation
}
public NoExposeExclusionStrategy() {
}
public boolean shouldSkipClass(Class<?> clazz) {
return (false);
}
public boolean shouldSkipField(FieldAttributes f) {
return f.getAnnotation(NoExpose.class) != null;
}
}
You can notice that with exclude fields, but that we can also exclude Classes. This can also be very interesting to exclude a model (so all datas of a particular table).
The main code is done. All we have to do now is just two basic actions.
-
change the declaration of our controller(s) :
public class Operations extends NoExposeStrategyController {
-annotate the fields we want to exclude :
@NoExpose
public String password;