23

Using Play Framework, I serialize my models via GSON. I specify which fields are exposed and which aren't.

This works great but I'd also like to @expose method too. Of course, this is too simple.

How can I do it ?

Thanks for your help !

public class Account extends Model {
    @Expose
    public String username;

    @Expose
    public String email;

    public String password;

    @Expose // Of course, this don't work
    public String getEncodedPassword() {
        // ...
    }
}
Jonik
  • 80,077
  • 70
  • 264
  • 372
Cyril N.
  • 38,875
  • 36
  • 142
  • 243

4 Answers4

24

The best solution I came with this problem was to make a dedicated serializer :

public class AccountSerializer implements JsonSerializer<Account> {

    @Override
    public JsonElement serialize(Account account, Type type, JsonSerializationContext context) {
        JsonObject root = new JsonObject();
        root.addProperty("id", account.id);
        root.addProperty("email", account.email);
        root.addProperty("encodedPassword", account.getEncodedPassword());

        return root;
    }

}

And to use it like this in my view:

GsonBuilder gson = new GsonBuilder();
gson.registerTypeAdapter(Account.class, new AccountSerializer());
Gson parser = gson.create();
renderJSON(parser.toJson(json));

But having @Expose working for a method would be great: it would avoid making a serializer just for showing methods!

Cyril N.
  • 38,875
  • 36
  • 142
  • 243
12

Check out Gson on Fire: https://github.com/julman99/gson-fire

It's a library I made that extends Gson to handle cases like exposing method, results Post-serialization, Post-deserialization and many other things that I've needed over time with Gson.

This library is used in production in our company Contactive (http://goo.gl/yueXZ3), on both Android and the Java Backend

julman99
  • 531
  • 5
  • 6
6

Couple different options based on Cyril's answer:

Custom serializer with a shortcut:

public static class Sample
{
    String firstName = "John";
    String lastName = "Doe";

    public String getFullName()
    {
        return firstName + " " + lastName;
    }
}

public static class SampleSerializer implements JsonSerializer<Sample>
{
    public JsonElement serialize(Sample src, Type typeOfSrc, JsonSerializationContext context)
    {
        JsonObject tree = (JsonObject)new Gson().toJsonTree(src);
        tree.addProperty("fullName", src.getFullName());
        return tree;
    }
}

public static void main(String[] args) throws Exception
{
    GsonBuilder gson = new GsonBuilder();
    gson.registerTypeAdapter(Sample.class, new SampleSerializer());
    Gson parser = gson.create();
    System.out.println(parser.toJson(new Sample()));

}

-OR- Annotation based serializer

public static class Sample
{
    String firstName = "John";
    String lastName = "Doe";

    @ExposeMethod
    public String getFullName()
    {
        return firstName + " " + lastName;
    }
}

public static class MethodSerializer implements JsonSerializer<Object>
{
    public JsonElement serialize(Object src, Type typeOfSrc, JsonSerializationContext context)
    {
        Gson gson = new Gson();
        JsonObject tree = (JsonObject)gson.toJsonTree(src);

        try
        {               
            PropertyDescriptor[] properties = Introspector.getBeanInfo(src.getClass()).getPropertyDescriptors();
            for (PropertyDescriptor property : properties)
            {
                if (property.getReadMethod().getAnnotation(ExposeMethod.class) != null)
                {
                    Object result = property.getReadMethod().invoke(src, (Object[])null);
                    tree.add(property.getName(), gson.toJsonTree(result));
                }
            }
        }
        catch (Exception ex)
        {
            ex.printStackTrace();
        }

        return tree;
    }
}

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD) //can use in method only.
public static @interface ExposeMethod {}

public static void main(String[] args) throws Exception
{
    GsonBuilder gson = new GsonBuilder();
    gson.registerTypeAdapter(Sample.class, new MethodSerializer());
    Gson parser = gson.create();
    System.out.println(parser.toJson(new Sample()));

}
craigrs84
  • 3,048
  • 1
  • 27
  • 34
  • This only works if there is only one class without any references to any other object. If you have for example a List, your method would not work. – Martin Feb 13 '15 at 11:07
6

Gson's @Expose seem to only be supported on fields. There is an issue registered on this: @Expose should be used with methods.

Jonas
  • 121,568
  • 97
  • 310
  • 388