【问题标题】:jackson - Is there any way to avoid serialization of parent entity at runtime?jackson - 有没有办法避免在运行时序列化父实体?
【发布时间】:2014-11-27 20:46:46
【问题描述】:

假设我有两个简单的类

@JsonFilter("filter properties by name")
public class Foo
{
    private Integer id;
    private String name;
}

@JsonFilter("filter properties by name")
public class Bar
{
    private Integer id;
    private String name;
    private Foo foo;
}

我想序列化Bar 的一个实例,其中foo 字段只有它的ID。这一切都应该在运行时完成。 我尝试使用过滤器来做到这一点

FilterProvider filter = new SimpleFilterProvider().addFilter(
            "filter properties by name", SimpleBeanPropertyFilter
                .serializeAllExcept(/*name of the field to exclude*/));
objectMapper.writer(filter).writeValuAsString(bar);

但是,这样做时,我必须手动排除父类的所有字段;此外,如果此字段之一与子类的字段名称相同,则它们都被排除在外。 在我的示例中,我不能以这种方式排除字段 name,因为它也会影响 Bar 类的字段 name

那么,我怎样才能以最简洁/优雅的方式解决? 有没有类似于上面的代码,可能使用点符号或类似的东西?

例如到现在为止,在我的示例过滤器中,我可以写类似[...].serializeAllExcept("name", "foo")); 的东西,但是拥有[...].serializeAllExcept("foo.name")); 或类似的东西真是太好了。

【问题讨论】:

    标签: java json jackson


    【解决方案1】:

    您不需要任何过滤器。 Foo 和 Bar 类没有变化。

    新的 MixInFoo 类:

    public class MixInFoo {
        @JsonProperty("mixinFooId")
        private Integer id;
        @JsonIgnore
        private String name;
    
    }
    

    我更改了 'id' 属性名称只是为了说明您可以完全更改响应而无需修改原始 Foo 类。

    ObjectMapper objectMapper = new ObjectMapper();
    objectMapper.getSerializationConfig().addMixInAnnotations(Foo.class, MixInFoo.class);
    objectMapper.getDeserializationConfig().addMixInAnnotations(Foo.class, MixInFoo.class);
    
    String result = objectMapper.writeValueAsString(json);
    

    您需要注册 MixIn 类,如图所示。

    ******* 基于过滤器的实现 ***********

    是的,您可以使用过滤器来获得相同的结果。您需要将@JsonFilter 添加到 Foo 类并将其命名为“FooFilter”。然后您可以添加仅适用于 Foo 类的过滤器:

    @JsonFilter("FooFilter")
    public class Foo {
        private Integer id;
        private String name;
    }
    
    public class Bar {
        private Integer id;
        private String name;
        private Foo foo;
    }
    
    public static void main(String []args) throws IOException {
        ObjectMapper objectMapper = new ObjectMapper();
    
        String []fooIgnore = {"name"};
        SimpleBeanPropertyFilter propertyFilter = SimpleBeanPropertyFilter.serializeAllExcept(fooIgnore);
        FilterProvider filterProvider = new SimpleFilterProvider().addFilter("FooFilter", propertyFilter);
    
        ObjectWriter objectWriter = objectMapper.writer(filterProvider);
        String result = objectWriter.writeValueAsString(json);
    
        System.out.println(result);
    }
    

    在该实现中,您无需扩展和实现自定义 SimpleBeanProvider 即可将过滤器仅应用于 Foo 类。

    【讨论】:

    • 但它不在运行时。我不想总是忽略这个领域,只是有时。
    • 谢谢,我一会儿试试。您认为是否可以仅使用 FilterProvider 而不使用任何其他类?
    【解决方案2】:

    可以使用过滤器。您可以编写一个自定义属性过滤器,该过滤器将考虑到序列化属性的声明类。

    您应该扩展 SimpleBeanPropertyFilter 并覆盖 include(PropertyWriter writer) 方法。如果给定的 writer 参数是 BeanPropertyWriter 类的实例,您可以提取有关属性来源的信息并应用您的自定义过滤逻辑。

    这是一个例子:

    public class JacksonParentFilter {
        @JsonFilter("filter")
        public static class A {
            public final String field1;
    
            public A(final String field1) {this.field1 = field1;}
        }
    
        @JsonFilter("filter")
        public static class B {
            public final String field1;
            public final String field2;
    
            public B(final String field1, final String field2) {
                this.field1 = field1;
                this.field2 = field2;
            }
        }
    
        public static class MyFilter extends SimpleBeanPropertyFilter {
            private final Class<?> excludeClass;
            private final Set<String> excludeProperties;
    
            public MyFilter(final Class<?> excludeClass, final Set<String> excludeProperties) {
                this.excludeClass = excludeClass;
                this.excludeProperties = excludeProperties;
            }
    
            @Override
            protected boolean include(final BeanPropertyWriter writer) {
                return false;
            }
    
            @Override
            protected boolean include(final PropertyWriter writer) {
                if (writer instanceof BeanPropertyWriter) {
                    final Class<?> cls = ((BeanPropertyWriter) writer).getMember().getDeclaringClass();
                    if (cls == excludeClass) {
                        return !excludeProperties.contains(writer.getName());
                    }
                }
                return true;
            }
        }
    
        public static void main(String[] args) throws JsonProcessingException {
            final A a = new A("A");
            final B b = new B("B1", "B2");
            final ObjectMapper mapper = new ObjectMapper();
            final SimpleFilterProvider filters = new SimpleFilterProvider();
            filters.addFilter("filter", new MyFilter(B.class, Collections.singleton("field1")));
            mapper.setFilters(filters);
            final ObjectWriter objectWriter = mapper.writerWithDefaultPrettyPrinter();
            System.out.println(objectWriter.writeValueAsString(Arrays.asList(a, b)));
        }
    
    }
    

    输出:

    [ {
      "field1" : "A"
    }, {
      "field2" : "B2"
    } ]
    

    【讨论】:

    • 感谢您抽出宝贵时间 Alexey。确实,它似乎工作得很好。但是,我希望有一些更简单的东西,也许使用点符号或类似的东西。例如。在我的示例过滤器中,我可以写类似[...].serializeAllExcept("field1", "field2")); 的东西,但是拥有[...].serializeAllExcept("field1", "field2.field1")); 或类似的东西真是太好了。这样,我不需要任何进一步的课程,一切看起来都更干净了。你觉得有可能吗?
    • 不,没有这种开箱即用的过滤器。您可以尝试根据我的示例创建自己的。
    猜你喜欢
    • 2012-04-21
    • 2014-02-01
    • 2011-01-12
    • 1970-01-01
    • 2011-06-23
    • 2021-12-15
    • 2016-01-04
    • 2019-09-07
    • 1970-01-01
    相关资源
    最近更新 更多