简体   繁体   English

杰克逊-有什么方法可以避免在运行时对父实体进行序列化?

[英]jackson - Is there any way to avoid serialization of parent entity at runtime?

Suppose I have two simple classes 假设我有两个简单的类

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

I'd like to serialize an instance of Bar in which the foo field has only its id. 我想序列化一个Bar实例,其中foo字段只有其ID。 It all should be done at runtime. 所有这些都应在运行时完成。 I tried to do this using filters 我试图使用过滤器做到这一点

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

However, in doing so, I have to manually exclude all the fields of the parent class; 但是,这样做时,我必须手动排除父类的所有字段。 also, if one of this field has the same name of a field of the child class, they are both excluded. 同样,如果此字段之一与子类的字段名称相同,则将它们都排除在外。 In my example, I can't exclude the field name in this way because it impacts also the field name of the Bar class. 在我的示例中,我不能以这种方式排除字段name ,因为它也会影响Bar类的字段name

So, how can I solve in a most concise/elegant way? 那么,我该如何以最简洁/优雅的方式解决? Is there something similar to the above code, maybe using dot notations or something like that? 是否有类似于上述代码的内容,也许使用了点符号或类似的东西?

Eg until now in my example filter I'm able to write something like [...].serializeAllExcept("name", "foo")); 例如,到目前为止,在示例过滤器中,我可以编写类似[...].serializeAllExcept("name", "foo")); , but it was great to have [...].serializeAllExcept("foo.name")); ,但是拥有[...].serializeAllExcept("foo.name")); or similar. 或类似。

You don't need any filters for it. 您不需要任何过滤器。 No changes in Foo and Bar classes. Foo和Bar类没有变化。

New MixInFoo class: 新的MixInFoo类:

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

}

I've changed 'id' property name just to illustrate that you can change response completely without modifying original Foo class. 我更改了'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);

You will need to register MixIn classes as shown. 您将需要注册MixIn类,如图所示。

******* Filter based implementation *********** ******* 基于过滤器的实现 ***********

Yes, you can use Filter to achieve the same result. 是的,您可以使用过滤器获得相同的结果。 You need to add @JsonFilter to the Foo class and name it like "FooFilter". 您需要将@JsonFilter添加到Foo类,并将其命名为“ FooFilter”。 Then you can add Filter that will apply only to the Foo class: 然后,您可以添加仅适用于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);
}

In that implementation you don't need to extend and implement you custom SimpleBeanProvider to apply the filter only to the Foo class. 在该实现中,您无需扩展和实现自定义SimpleBeanProvider即可将过滤器仅应用于Foo类。

It is possible using filters. 可以使用过滤器。 You can write a custom property filter which would take into account the declaring class of the serialised properties. 您可以编写一个自定义属性过滤器,该过滤器将考虑序列化属性的声明类。

You should extend the SimpleBeanPropertyFilter and override the include(PropertyWriter writer) method. 您应该扩展SimpleBeanPropertyFilter并重写include(PropertyWriter writer)方法。 If the given writer parameter is an instance of BeanPropertyWriter class, you can extract the information about the property origin and apply your custom the filtering logic. 如果给定的writer参数是BeanPropertyWriter类的实例,则可以提取有关属性来源的信息,并应用自定义过滤逻辑。

Here is an example: 这是一个例子:

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

}

Output: 输出:

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

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM