【问题标题】:How to include only specific properties when serializing with Jackson使用 Jackson 序列化时如何仅包含特定属性
【发布时间】:2014-12-17 23:54:36
【问题描述】:

我正在尝试实现一种将给定对象序列化为 JSON 的通用方法,但仅限于在集合中传递的那些属性。如果可能的话,我想在不指定 @JsonFilter 的情况下获得此功能。为此,我尝试使用 Jackson 2.4.1 中的 FilterExceptFilter。依赖:

这是我目前拥有的:

public static String serializeOnlyGivenFields(Object o,
                    Collection<String> fields) throws JsonProcessingException {
    if ((fields == null) || fields.isEmpty()) return null;

    Set<String> properties = new HashSet<String>(fields);

    SimpleBeanPropertyFilter filter =
        new SimpleBeanPropertyFilter.FilterExceptFilter(properties);
    SimpleFilterProvider fProvider = new SimpleFilterProvider();
    fProvider.addFilter("fieldFilter", filter);
    fProvider.setDefaultFilter(filter);

    ObjectMapper mapper = new ObjectMapper();
    mapper.setFilters(fProvider);

    String json = mapper.writeValueAsString(o);
    return json;
}

但是,过滤器永远不会应用。它总是序列化所有属性。

Set<String> fields = new HashSet<String>(); fields.add("name");
String json = Serializer.serializeOnlyGivenFields(e, fields);
System.out.println(json);

{"name":"测试实体","description":"测试描述"}

我也尝试注册FilterProvider on the ObjectWriter,但结果相同:

String json = mapper.writer(fProvider).writeValueAsString(o);

我错过了什么?有没有一种很好的方法可以通过 Jackson 实现这一目标?

【问题讨论】:

    标签: json serialization filter jackson


    【解决方案1】:

    基于http://www.cowtowncoder.com/blog/archives/2011/09/entry_461.html,另一种设置过滤器的方法是设置一个扩展JacksonAnnotationIntrospector 并覆盖findFilterId 的类。然后,您可以指定在 findFilterId 中查找您的过滤器。如果您希望基于其他一些地图或算法,这可以变得同样健壮。下面是示例代码。不确定性能是否比上面的解决方案更好,但它似乎更简单并且可能更容易扩展。我这样做是为了使用 Jackson 序列化 CSV。欢迎任何反馈!

    public class JSON {
    
    private static String FILTER_NAME = "fieldFilter";
    
    public static String serializeOnlyGivenFields(Object o,
                                                  Collection<String> fields) throws JsonProcessingException {
        if ((fields == null) || fields.isEmpty()) fields = new HashSet<String>();
    
        Set<String> properties = new HashSet<String>(fields);
    
        SimpleBeanPropertyFilter filter =
                new SimpleBeanPropertyFilter.FilterExceptFilter(properties);
        SimpleFilterProvider fProvider = new SimpleFilterProvider();
        fProvider.addFilter(FILTER_NAME, filter);
    
        ObjectMapper mapper = new ObjectMapper();
        mapper.setAnnotationIntrospector( new AnnotationIntrospector() );
    
        String json = mapper.writer(fProvider).writeValueAsString(o);
        return json;
    }
    
    private static class AnnotationIntrospector extends JacksonAnnotationIntrospector {
        @Override
        public Object findFilterId(Annotated a) {
            return FILTER_NAME;
        }
    }
    
    }
    

    【讨论】:

    • 你太棒了!你去哪儿了?
    【解决方案2】:

    另外一件事是,您必须通过@JsonFilter 注解指明要使用过滤器的 Java 类:

    @JsonFilter("fieldFilter") public class MyType { }

    然后它应该适用。

    【讨论】:

    • 所以不使用@JsonFilter就没有通用的方法来实现这一点?
    • 那个或@JsonView 可能是你最好的选择。 cowtowncoder.com/blog/archives/2011/02/entry_443.html 列出了输出过滤的常用机制。
    • 哦,我真的只是在读那篇文章 :) 我对@JsonView 的问题是,如果应用,没有定义视图的属性无论如何都会被序列化。这给复杂的类层次结构带来了麻烦。另一方面,我看不到 @JsonFilter 注释的意义。我的意思是您在 ObjectWriter 上应用过滤器实现,然后该过滤器的名称也被定义为注释,对于 erm...究竟是什么?
    • 要知道应用哪个过滤器?可能有多个过滤器(针对不同的类);以及大多数类可能根本不需要过滤。部分是为了性能;过滤是一个附加选项,有一些开销,默认情况是不进行过滤检查或处理的情况。没有“通用过滤器”,但如果你确实想要这样的东西,你可以通过几种方式来实现它(例如 AnnotationIntrospector 的子类以声称所有类都有 @JsonFilter)。
    • 请看我的回答。你怎么看?
    【解决方案3】:

    我找到了基于Jackson: How to add custom property to the JSON without modifying the POJO 的解决方案。我覆盖 BeanSerializer#serializeFields 以始终使用 BeanSerializer#serializeFieldsFiltered 代替。这样过滤器就会一直应用。

    性能方面不是一个很好的解决方案,因为必须在每次方法调用时构造一个ObjectMapper。随时发布改进或建议!

    模块实现:

    public class FilteredModule extends SimpleModule {
        private static final long serialVersionUID = 1L;
    
        @Override
        public void setupModule(SetupContext context) {
            super.setupModule(context);
    
            context.addBeanSerializerModifier(new BeanSerializerModifier() {
    
                @Override
                public JsonSerializer<?> modifySerializer(
                        SerializationConfig config,
                        BeanDescription beanDesc,
                        JsonSerializer<?> serializer) {
                    if (serializer instanceof BeanSerializerBase) { 
                        return new FilteredBeanSerializer(
                                (BeanSerializerBase) serializer);
                    } 
                    return serializer; 
    
                }                   
            });
        }
    
        private class FilteredBeanSerializer extends BeanSerializer {
    
            public FilteredBeanSerializer(BeanSerializerBase source) {
                super(source);
            }
    
            @Override
            protected void serializeFields(Object arg0, JsonGenerator arg1,
                    SerializerProvider arg2) throws IOException,
                    JsonGenerationException {
                super.serializeFieldsFiltered(arg0, arg1, arg2);
            }
    
        }
    }
    

    API 方法:

    public static String serializeOnlyGivenFields(Object o,
                Collection<String> fields) throws JsonProcessingException {
        if ((fields == null) || fields.isEmpty()) fields = new HashSet<String>();
    
        Set<String> properties = new HashSet<String>(fields);
    
        SimpleBeanPropertyFilter filter =
                new SimpleBeanPropertyFilter.FilterExceptFilter(properties);
        SimpleFilterProvider fProvider = new SimpleFilterProvider();
        fProvider.addFilter("fieldFilter", filter);
        fProvider.setDefaultFilter(filter);
    
        ObjectMapper mapper = new ObjectMapper();
        mapper.registerModule(new FilteredModule());
    
        String json = mapper.writer(fProvider).writeValueAsString(o);
        return json;
    }
    

    示例

    Entity e = new Entity("Test entity", "Test description");   
    Set<String> fields = new HashSet<String>(); fields.add("name");
    String json = JSON.serializeOnlyGivenFields(e, fields);
    System.out.println(json);
    

    {"name":"测试实体"}

    基准测试: 对同一对象进行 1000 次迭代

    serializeOnlyGivenFields:           536 ms
    serialize (reuses ObjectMapper):    23 ms
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2019-06-08
      • 2014-11-11
      • 1970-01-01
      • 2016-08-15
      • 1970-01-01
      • 2017-07-10
      • 2020-08-01
      • 1970-01-01
      相关资源
      最近更新 更多