jackson - 有没有办法在运行时避免父实体的序列化?

时间:2014-11-27 20:46:47

标签: java json jackson

假设我有两个简单的类

@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,因为它还会影响name类的字段Bar

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

E.g。到目前为止,在我的示例过滤器中,我能够编写类似[...].serializeAllExcept("name", "foo"));的内容,但拥有[...].serializeAllExcept("foo.name"));或类似内容非常棒。

2 个答案:

答案 0 :(得分:2)

您不需要任何过滤器。 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类,如图所示。

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

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

@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类。

答案 1 :(得分:1)

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

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

以下是一个例子:

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"
} ]