给定以下类层次结构,我希望根据类层次结构中使用的上下文,对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; }
您可以结合使用自定义序列化程序和使用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:
@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,将执行以下两项操作的自定义:
AnnotationIntrospector
@FilterUsingView
这是代码
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()); }