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

给定以下类层次结构,我希望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,但必须在mapper层应用于类的所有实例,这与上下文有关。


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

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

您可以使用JsonViews将自定义序列化程序与自定义属性filter结合使用。 这是一些与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; } 

然后,这里是代码开始的地方:)首先你的自定义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 ,它将做两件事:

  1. 为任何bean启用自定义filter…除非在您的类上定义了另一个filter(因此您不能同时使用它们,如果您看到我的意思)
  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()); } } 

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

 public class CustomSerializer extends JsonSerializer { 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()); } 
 import static org.junit.Assert.*; import java.io.IOException; import java.io.StringWriter; import java.lang.reflect.Type; import org.codehaus.jackson.JsonGenerationException; import org.codehaus.jackson.JsonGenerator; import org.codehaus.jackson.JsonNode; import org.codehaus.jackson.map.JsonMappingException; import org.codehaus.jackson.map.ObjectMapper; import org.codehaus.jackson.map.SerializerProvider; import org.codehaus.jackson.map.annotate.JsonSerialize; import org.codehaus.jackson.map.ser.SerializerBase; import org.junit.Test; class Foo { public String bar; public String biz; } class FooContainer { public Foo fooA; @JsonSerialize(using = FooCustomSerializer.class) public Foo fooB; } class FooCustomSerializer extends SerializerBase { public FooCustomSerializer() { super(Foo.class); } @Override public void serialize(Foo foo, JsonGenerator generator, SerializerProvider provider) throws IOException, JsonGenerationException { generator.writeStartObject(); generator.writeObjectField("bar", foo.bar); generator.writeEndObject(); } @Override public JsonNode getSchema(SerializerProvider arg0, Type arg1) throws JsonMappingException { return null; } } public class JacksonTest { @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(); StringWriter writer = new StringWriter(); mapper.writeValue(writer, object); String expected = "{\"fooA\":{\"bar\":\"asdf\",\"biz\":\"fdsa\"},\"fooB\":{\"bar\":\"qwer\"}}"; assertEquals(expected, writer.toString()); } } 

在公共Foo fooB上使用@JsonSerialize(使用= FooCustomSerializer.class); 领域。

http://jackson.codehaus.org/1.9.9/javadoc/org/codehaus/jackson/map/annotate/JsonSerialize.html

我会使用谷歌代码gson
这里的文档https://code.google.com/p/google-gson/
Maven依赖是:

  com.google.code.gson gson 2.2.1  

注释是这样的:
要向字段用户公开@Expose注释
要在解析的json用户中为字段生成特殊名称,请使用@SerializedName("fieldNameInJSON")注释
所以你的类看起来像这样:

  public class Foo { @SerializedName("bar") @Expose public String bar; @SerializedName("biz") @Expose public String biz; } public class FooContainer { @SerializedName("fooA") @Expose public Foo fooA; @SerializedName("fooB") @Expose public Foo fooB; } 

要序列化为JSON,您将使用如下所示的代码:

 public String convertToJSON(FooContainer fc) { if (fc != null) { GsonBuilder gson = new GsonBuilder(); return gson.excludeFieldsWithoutExposeAnnotation().create().toJson(fc); } return ""; } 

例如,列表看起来相同:

 public String convertToJSON(List fcs) { if (fcs != null) { GsonBuilder gson = new GsonBuilder(); return gson.excludeFieldsWithoutExposeAnnotation().create().toJson(fcs); } return ""; }