如何使用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
,它将做两件事:
- 为任何bean启用自定义filter…除非在您的类上定义了另一个filter(因此您不能同时使用它们,如果您看到我的意思)
- 如果找到
@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
最后! 让我们把这一切放在一起:
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 ""; }