如何使用Jackson和注释以不同方式序列化关联对象

如何使用Jackson和注释以不同方式序列化关联对象

本文介绍了如何使用Jackson和注释以不同方式序列化关联对象?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

Given the following class hierarchy, I would like Foo to be serialized differently depending on the context it is used in my class hierarchy.

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

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

我希望在序列化时,biz属性不会显示在fooB中FooContainer。所以输出看起来如下所示。

I would like for the biz attribute to not show up in fooB when I serialize FooContainer. So the output would look something like the following.

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

我打算使用JsonView,但必须在mapper层应用于所有类的实例,这与上下文有关。

I was going to use something JsonView, but that has to be applied at the mapper layer for all instances of a class, and this is context dependent.

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

On the Jackson user mailing list, Tatu gave the simplest solution (works in 2.0), which I will probably end up using for now. Awarding the bounty to jlabedo because the answer is an awesome example of how to extend Jackson using custom annotations.

public class FooContainer {
    public Foo fooA;

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


推荐答案

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

You could use a combination of a custom serializer with a custom property filter using JsonViews. Here is some code working with 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

Then you can write your entities like this. Note that you may define your own annotation instead of using @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;
}

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

Then, here is where the code begins :)First your custom filter:

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 这将做两件事:

Then a custom AnnotationIntrospector that will do two things:


  1. 为任何bean启用自定义过滤器...除非定义了另一个过滤器你的班级(如果你明白我的意思,你就不能同时使用它们)。

  2. 如果找到 @FilterUsingView 注释。

  1. Enable your custom filter for any bean... Unless another filter is defined on your class (so you cannot use both of them, if you see what I mean)
  2. Enable your CustomSerializer if he found a @FilterUsingView annotation.

这是代码

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

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

Here is your custom serializer. The only thing it does is passing your annotation's value to your custom filter, then it let the default serializer do the job.

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

最后!让我们把它们放在一起:

Finally ! Let's put this all together :

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

这篇关于如何使用Jackson和注释以不同方式序列化关联对象?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!

09-02 11:02