Rendering JSON output

As soon as a web application consists of a very fast frontend, no or seldom complete page reloads occur. This implies a complete rendering by the browser, which is one of the most time consuming tasks when loading a webpage in the browser. This means you have to get the data as fast as possible to the client. You can either send them as pre-rendered HTML, XML, or JSON format. Sending the data in either JSON or XML means the application on the browser side can render the data itself and decide how and when it should be displayed. This means your application should be able to create JSON or XML-based responses.

As JSON is quite popular, this example will not only show you how to return the JSON representation of an entity, but also how to make sure sensitive data such as a password will not get sent to the user.

Furthermore some hypermedia content will be added to the response, like an URL where more information can be found.

You can find the source code of this example in the chapter2/json-render-properties directory.

Getting ready

Beginning with a test is always a good idea:

public class JsonRenderTest extends FunctionalTest {

    @Test
    public void testThatJsonRenderingWorks() {
        Response response = GET("/user/1");
        assertIsOk(response);

        User user = new Gson().fromJson(getContent(response), User.class);
        assertNotNull(user);
        assertNull(user.password);
        assertNull(user.secrets);
        assertEquals(user.login, "alex");
        assertEquals(user.address.city, "Munich");
        assertContentMatch(""uri":"/user/1"", response);
    }
}

This expects a JSON reply from the request and parses it into a User instance with the help of gson, a JSON library from Google, which is also used by Play for serializing. As we want to make sure that no sensitive data is sent, there is a check for nullified values of password and secrets properties. The next check goes for a user property and for a nested property inside another object. The last check has to be done by just checking for an occurrence of the string, because the URL is not a property of the user entity and is dynamically added by the special JSON serializing routine used in this example.

How to do it...

Create your entities first. This example consists of a user, an address, and a SuperSecretData entity:

@Entity
public class User extends Model {

    @SerializedName("userLogin")
    public String login;
    @NoJsonExport
    public String password;
    @ManyToOne
    public Address address;
    @OneToOne
    public SuperSecretData secrets;

    public String toString() {
        return id + "/" +  login;
    }
}

@Entity
public class Address extends Model {
    public String street;
    public String city;
    public String zip;
}

@Entity
public class SuperSecretData extends Model {
    public String secret = "foo";
}

The controller is simple as well:

public static void showUser(Long id) {
    User user = User.findById(id);
    notFoundIfNull(user);
    renderJSON(user, new UserSerializer());
}

The last and most important part is the serializer used in the controller above:

public class UserSerializer implements JsonSerializer<User> {

    public JsonElement serialize(User user, Type type,
        JsonSerializationContext context) {
        Gsongson = new GsonBuilder()
            .setExclusionStrategies(new LocalExclusionStrategy())
            .create();

      JsonElementelem = gson.toJsonTree(user);
      elem.getAsJsonObject().addProperty("uri", createUri(user.id));
      return elem;
    }

    private String createUri(Long id) {
      Map<String,Object> map = new HashMap<String,Object>();
      map.put("id", id);
      return Router.reverse("Application.showUser", map).url;
    }


    public static class LocalExclusionStrategy implements ExclusionStrategy {

      public booleanshouldSkipClass(Class<?>clazz) {
        return clazz == SuperSecretData.class;
      }

      public booleanshouldSkipField(FieldAttributes f) {
        return f.getAnnotation(NoJsonExport.class) != null;
      }
    }
}

How it works...

The entities used in this example are simple. The only differences are the two annotations in the User entity. First there is a SerializedNamed annotation, which uses the annotation argument as field name in the json output – this annotation comes from the gson library. The @NoJsonExport annotation has been specifically created in this example to mark fields that should not be exported like a sensitive password field in this example. The address field is only used as an example to show how many-to-many relations are serialized in the JSON output.

As you might guess, the SuperSecretData class should mark the data as secret, so this field should not be exported as well. However, instead of using an annotation, the functions of the Google gson library will be utilized for this.

The controller call works like usual except that the renderJson() method gets a specific serializer class as argument to the object it should serialize.

The last class is the UserSerializer class, which is packed with features, although it is quite short. As the class implements the JsonSerializer class, it has to implement the serialize() method. Inside of this method a gson builder is created, and a specific exclusion strategy is added. After that the user object is automatically serialized by the gson object. Lastly another property is added. This property is the URI of the showUser() controller call, in this case something like /user/{id} . You can utilize the Play internal router to create the correct URL.

The last part of the serializer is the ExclusionStrategy, which is also a part of the gsonserializer. This strategy allows exclusion of certain types of fields. In this case the method shouldSkipClass() excludes every occurrence of the SuperSecretData class, where the method shouldSkipFields() excludes fields marked with the @NoJsonExport annotation.

There's more...

If you do not want to write your own JSON serializer you could also create a template ending with .json and write the necessary data like in a normal HTML template. However there is no automatic escaping, so you would have to take care of that yourself.

More about Google gson

Google gson is a pretty powerful JSON library. Once you get used to it and learn to utilize its features it may help you to keep your code clean. It has very good support for specific serializers and deserializers, so make sure you check out the documentation before trying to build something for yourself. Read more about it at http://code.google.com/p/google-gson/.

Alternatives to Google gson

Many developers do not like the gson library at all. There are several alternatives. There is a nice example of how to integrate FlexJSON. Check it out at http://www.lunatech-research.com/archives/2011/04/20/play-framework-better-json-serialization-flexjson.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.21.46.78