jackson: ignore getter, but not with @JsonView

Anton Bessonov picture Anton Bessonov · Apr 19, 2014 · Viewed 7.1k times · Source

I'm looking for possibility to serialize transient information only in some cases:

@JsonInclude(Include.NON_NULL)
@Entity
public class User {

    public static interface AdminView {}

    ... id, email and others ...

    @Transient
    private transient Details details;

    @JsonIgnore                  // Goal: ignore all the time, except next line
    @JsonView(AdminView.class)   // Goal: don't ignore in AdminView
    public Details getDetails() {
        if (details == null) {
            details = ... compute Details ...
        }
        return details;
    }
}

public class UserDetailsAction {
    private static final ObjectWriter writer = new ObjectMapper();
    private static final ObjectWriter writerAdmin = writer
        .writerWithView(User.AdminView.class);

    public String getUserAsJson(User user) {
        return writer.writeValueAsString(user);
    }

    public String getUserAsJsonForAdmin(User user) {
        return writerAdmin.writeValueAsString(user);
    }
}

If I call getUserAsJson I expected to see id, email and other fields, but not details. This works fine. But I see same for getUserAsJsonForAdmin, also without detail. If I remove @JsonIgnore annotation - I do see details in both calls.

What do I wrong and is there good way to go? Thanks!

Answer

Alexey Gavrilov picture Alexey Gavrilov · Apr 20, 2014

You may find the use of the dynamic Jackson filtering slightly more elegant for your use case. Here is an example of the filtering of POJO fields based on a custom annotation sharing one object mapper instance:

public class JacksonFilter {
    static private boolean shouldIncludeAllFields;

    @Retention(RetentionPolicy.RUNTIME)
    public static @interface Admin {}

    @JsonFilter("admin-filter")
    public static class User {
        public final String email;
        @Admin
        public final String details;

        public User(String email, String details) {
            this.email = email;
            this.details = details;
        }
    }

    public static class AdminPropertyFilter extends SimpleBeanPropertyFilter {

        @Override
        protected boolean include(BeanPropertyWriter writer) {
            // deprecated since 2.3
            return true;
        }

        @Override
        protected boolean include(PropertyWriter writer) {
            if (writer instanceof BeanPropertyWriter) {
                return shouldIncludeAllFields || ((BeanPropertyWriter) writer).getAnnotation(Admin.class) == null;
            }
            return true;
        }
    }

    public static void main(String[] args) throws JsonProcessingException {
        User user = new User("email", "secret");
        ObjectMapper mapper = new ObjectMapper();
        mapper.setFilters(new SimpleFilterProvider().addFilter("admin-filter", new AdminPropertyFilter()));
        System.out.println(mapper.writerWithDefaultPrettyPrinter().writeValueAsString(user));
        shouldIncludeAllFields = true;
        System.out.println(mapper.writerWithDefaultPrettyPrinter().writeValueAsString(user));
    }

}

Output:

{
  "email" : "email"
}
{
  "email" : "email",
  "details" : "secret"
}