【问题标题】:jackson: ignore getter, but not with @JsonView杰克逊:忽略getter,但不使用@JsonView
【发布时间】:2014-06-04 00:47:37
【问题描述】:

我正在寻找仅在某些情况下序列化瞬态信息的可能性:

@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,我希望看到 id、email 和其他字段,但看不到详细信息。这工作正常。但我对 getUserAsJsonForAdmin 也看到了同样的情况,也没有详细说明。如果我删除 @JsonIgnore 注释 - 我确实会在两个调用中看到详细信息。

我做错了什么,有什么好的方法吗?谢谢!

【问题讨论】:

    标签: java json serialization jackson json-view


    【解决方案1】:

    看起来杰克逊对@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);
        }
    }
    

    也许它对某人有帮助。但我希望找到更好的解决方案,因为不接受我自己的答案。

    编辑:因为接口可以扩展(多个)接口,我可以使用:

    public static interface AdminView extends BasicView {}
    

    只是

    @JsonView(BasicView.class)
    

    而不是

    @JsonView({BasicView.class, AdminView.class})
    

    【讨论】:

    • 如何为作为 REST 响应发送的 POJO 执行此操作?注意:我没有使用 ObjectWriter
    • 使用 null 而不是某些特定视图会有所帮助吗? @JsonView(null) // 目标:一直忽略,除了下一行
    【解决方案2】:

    对于您的用例,您可能会发现使用 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"
    }
    

    【讨论】:

    • 好吧,在测试了不同的解决方案之后,自定义过滤器和FilterIntrospector似乎是我想走的一条路。
    • 对于跨线程使用此代码的任何人,使用 shouldIncludeAllFields 存在潜在的并发问题。但只需创建 2 个 ObjectMapper 即可轻松修复,1 个带过滤器,1 个不带。 ObjectMappers 是线程安全的
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-11-03
    • 1970-01-01
    • 2016-04-19
    • 2016-01-16
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多