2014-04-19 2 views
5

Я ищу возможность сериализацию переходной информации только в некоторых случаях:джексон: игнорировать добытчик, но не с @JsonView

@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); 
    } 
} 

Если я позвоню getUserAsJson я ожидал увидеть идентификационный код, электронные и другие области, но не детали. Это прекрасно работает. Но я вижу то же самое для getUserAsJsonForAdmin, также без деталей. Если я удалю аннотацию @JsonIgnore - я вижу подробности в обоих вызовах.

Что я не так, и есть ли хороший способ? Благодаря!

ответ

4

Вы можете найти использование the dynamic Jackson filtering немного более элегантно для вашего случая использования , Вот пример фильтрации полей POJO на основе пользовательских аннотаций обмена экземпляра сопоставителя один объект:

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)); 
    } 

} 

Выход:

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

Спасибо за хороший пример! –

+0

Хорошо, после тестирования различных решений, настраиваемый фильтр и FilterIntrospector, похоже, так я и хочу. –

+0

Для всех, кто использует этот код в потоках, существует потенциальная проблема параллелизма с использованием shouldIncludeAllFields. Но легко фиксируется просто созданием 2 ObjectMappers, 1 с фильтром и 1 без. ObjectMappers - Threadsafe – muttonUp

0

Это похоже, что у джексона есть ужасная концепция на очень крутой функции, такой как @JsonView. Единственный способ, который я обнаружил для решения своей проблемы, - это:

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

    public static interface BasicView {} 
    public static interface AdminView {} 

    ... id and others ... 

    @JsonView({BasicView.class, AdminView.class}) // And this for EVERY field 
    @Column 
    private String email; 

    @Transient 
    private transient Details details; 

    @JsonView(AdminView.class) 
    public Details getDetails() { 
     if (details == null) { 
      details = ... compute Details ... 
     } 
     return details; 
    } 
} 

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

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

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

Возможно, это помогает кому-то. Но я надеюсь найти лучшее решение и потому, что не согласен с моим собственным ответом.

EDIT: потому, что интерфейс может расширяет (несколько) интерфейсов, я могу использовать:

public static interface AdminView extends BasicView {} 

и просто

@JsonView(BasicView.class) 

вместо того, чтобы

@JsonView({BasicView.class, AdminView.class}) 
Смежные вопросы