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;
comments powered by Disqus