一尘不染

如何使用Jackson和注释不同地序列化关联对象?

java

给定以下类层次结构,我希望根据类层次结构中使用的上下文,对Foo进行不同的序列化。

public class Foo {
    public String bar;
    public String biz;
}

public class FooContainer {
    public Foo fooA;
    public Foo fooB;
}

我希望序列化FooContainer时biz属性不显示在fooB中。因此,输出将类似于以下内容。

{
  "fooA": {"bar": "asdf", "biz": "fdsa"},
  "fooB": {"bar": "qwer"}
}

我本打算使用JsonView,但是必须将其应用于类的所有实例的映射器层,并且这取决于上下文。


在Jackson用户的邮件列表中,Tatu提供了最简单的解决方案(在2.0中可用),我现在可能最终会使用它。将奖励授予jlabedo,因为答案是一个如何使用自定义注释扩展Jackson的绝妙示例。

public class FooContainer {
    public Foo fooA;

    @JsonIgnoreProperties({ "biz" })
    public Foo fooB;
}

阅读 192

收藏
2020-12-03

共1个答案

一尘不染

您可以结合使用自定义序列化程序和使用JsonViews的自定义属性过滤器。这是使用Jackson 2.0的一些代码

定义一个自定义注释:

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.FIELD)
public @interface FilterUsingView {
    Class<?>[] value();
}

定义一些视图:

// Define your views here
public static class Views {
    public class Public {};
    public class Internal extends Public{};
}

然后,您可以像这样编写实体。请注意,您可以定义自己的注释,而不是使用@JsonView

public class Foo {
    @JsonView(Views.Public.class)
    public String bar;
    @JsonView(Views.Internal.class)
    public String biz;
}

public class FooContainer {
    public Foo fooA;
    @FilterUsingView(Views.Public.class)
    public Foo fooB;
}

然后,这是代码开始的地方:)首先,您的自定义过滤器:

public static class CustomFilter extends SimpleBeanPropertyFilter {

    private Class<?>[] _nextViews;

    public void setNextViews(Class<?>[] clazz){
        _nextViews = clazz;
    }

    @Override
    public void serializeAsField(Object bean, JsonGenerator jgen,
            SerializerProvider prov, BeanPropertyWriter writer)
            throws Exception {

        Class<?>[] propViews = writer.getViews();

        if(propViews != null && _nextViews != null){
            for(Class<?> propView : propViews){
                System.out.println(propView.getName());
                for(Class<?> currentView : _nextViews){
                    if(!propView.isAssignableFrom(currentView)){
                        // Do the filtering!
                        return;
                    }
                }
            }
        }
        // The property is not filtered
        writer.serializeAsField(bean, jgen, prov);
    }
}

然后AnnotationIntrospector,将执行以下两项操作的自定义:

  1. 为任何bean启用自定义过滤器…除非在类上定义了另一个过滤器(如果您明白我的意思,则不能同时使用它们)
  2. 如果他发现了@FilterUsingView注释,则启用您的CustomSerializer 。

这是代码

public class CustomAnnotationIntrospector extends AnnotationIntrospector {
    @Override
    public Version version() {
        return DatabindVersion.instance.version();
    }

    @Override
    public Object findFilterId(AnnotatedClass ac) {
      // CustomFilter is used for EVERY Bean, unless another filter is defined
      Object id = super.findFilterId(ac);
      if (id == null) {
        id = "CustomFilter";
      }
      return id;
    }

    @Override
    public Object findSerializer(Annotated am) {
        FilterUsingView annotation = am.getAnnotation(FilterUsingView.class);
        if(annotation == null){
            return null;
        }
        return new CustomSerializer(annotation.value());
    }
}

这是您的自定义序列化程序。它唯一要做的就是将注释的值传递给自定义过滤器,然后让默认的序列化程序完成这项工作。

public class CustomSerializer extends JsonSerializer<Object> {

    private Class<?>[] _activeViews;

    public CustomSerializer(Class<?>[] view){
        _activeViews = view;
    }

    @Override
    public void serialize(Object value, JsonGenerator jgen,
            SerializerProvider provider) throws IOException,
            JsonProcessingException {

        BeanPropertyFilter filter = provider.getConfig().getFilterProvider().findFilter("CustomFilter");
        if(filter instanceof CustomFilter){
            CustomFilter customFilter = (CustomFilter) filter;

            // Tell the filter that we will filter our next property
            customFilter.setNextViews(_activeViews);

            provider.defaultSerializeValue(value, jgen);

            // Property has been filtered and written, do not filter anymore
            customFilter.setNextViews(null);
        }else{
            // You did not define a CustomFilter ? Well this serializer is useless...
            provider.defaultSerializeValue(value, jgen);
        }
    }
}

终于!让我们放在一起:

public class CustomModule extends SimpleModule {

    public CustomModule() {
        super("custom-module", new Version(0, 1, 0, "", "", ""));
    }

    @Override
    public void setupModule(SetupContext context) {
        super.setupModule(context);
        AnnotationIntrospector ai = new CustomAnnotationIntrospector();
        context.appendAnnotationIntrospector(ai);
    }

}



@Test
public void customField() throws Exception {
    FooContainer object = new FooContainer();
    object.fooA = new Foo();
    object.fooA.bar = "asdf";
    object.fooA.biz = "fdsa";
    object.fooB = new Foo();
    object.fooB.bar = "qwer";
    object.fooB.biz = "test";

    ObjectMapper mapper = new ObjectMapper();
    mapper.registerModule(new CustomModule());

    FilterProvider fp = new SimpleFilterProvider().addFilter("CustomFilter", new CustomFilter());
    StringWriter writer = new StringWriter();

    mapper.writer(fp).writeValue(writer, object);

    String expected = "{\"fooA\":{\"bar\":\"asdf\",\"biz\":\"fdsa\"},\"fooB\":{\"bar\":\"qwer\"}}";

    Assert.assertEquals(expected, writer.toString());
}
2020-12-03