jackson动态属性名称

我想序列化一个对象,以便根据字段的类型对其中一个字段进行不同的命名。 例如:

public class Response { private Status status; private String error; private Object data; [ getters, setters ] } 

在这里,我希望将字段data序列化为类似data.getClass.getName()而不是总是有一个名为data的字段,根据情况包含不同的类型。

我如何使用Jackson实现这样的技巧?

使用自定义JsonSerializer

 public class Response { private String status; private String error; @JsonProperty("p") @JsonSerialize(using = CustomSerializer.class) private Object data; // ... } public class CustomSerializer extends JsonSerializer { public void serialize(Object value, JsonGenerator jgen, SerializerProvider provider) throws IOException, JsonProcessingException { jgen.writeStartObject(); jgen.writeObjectField(value.getClass().getName(), value); jgen.writeEndObject(); } } 

然后,假设您要序列化以下两个对象:

 public static void main(String... args) throws Exception { ObjectMapper mapper = new ObjectMapper(); Response r1 = new Response("Error", "Some error", 20); System.out.println(mapper.writeValueAsString(r1)); Response r2 = new Response("Error", "Some error", "some string"); System.out.println(mapper.writeValueAsString(r2)); } 

第一个将打印:

 {"status":"Error","error":"Some error","p":{"java.lang.Integer":20}} 

第二个:

 {"status":"Error","error":"Some error","p":{"java.lang.String":"some string"}} 

我已经使用名称p作为包装器对象,因为它仅用作p laceholder。 如果要删除它,则必须为整个类编写自定义序列化程序,即JsonSerializer

我使用@JsonAnyGetter注释有一个更简单的解决方案,它就像一个魅力。

 import java.util.Collections; import java.util.Map; public class Response { private Status status; private String error; @JsonIgnore private Object data; [getters, setters] @JsonAnyGetter public Map any() { //add the custom name here //use full HashMap if you need more than one property return Collections.singletonMap(data.getClass().getName(), data); } } 

不需要包装器,不需要自定义序列化器。

我自己的解决方案

 @Data @EqualsAndHashCode @ToString @JsonSerialize(using = ElementsListBean.CustomSerializer.class) public class ElementsListBean { public ElementsListBean() { } public ElementsListBean(final String fieldName, final List elements) { this.fieldName = fieldName; this.elements = elements; } private String fieldName; private List elements; public int length() { return (this.elements != null) ? this.elements.size() : 0; } private static class CustomSerializer extends JsonSerializer { public void serialize(Object value, JsonGenerator jgen, SerializerProvider provider) throws IOException, JsonProcessingException { if (value instanceof ElementsListBean) { final ElementsListBean o = (ElementsListBean) value; jgen.writeStartObject(); jgen.writeArrayFieldStart(o.getFieldName()); for (Object e : o.getElements()) { jgen.writeObject(e); } jgen.writeEndArray(); jgen.writeNumberField("length", o.length()); jgen.writeEndObject(); } } } } 

您可以使用注释JsonTypeInfo ,它完全告诉Jackson,您不需要编写自定义序列化程序。 有多种方法可以包含此信息,但针对您的具体问题,您可以使用As.WRAPPER_OBJECTId.CLASS 。 例如:

 public static class Response { private Status status; private String error; @JsonTypeInfo(include = As.WRAPPER_OBJECT, use = Id.CLASS) private Object data; } 

但是,这不适用于基本类型,例如String或Integer。 无论如何,您不需要原语的信息,因为它们本身用JSON表示,Jackson知道如何处理它们。 使用注释的额外好处是,如果您需要,可以免费进行反序列化。 这是一个例子:

 public static void main(String[] args) throws Exception { ObjectMapper mapper = new ObjectMapper(); Response r1 = new Response("Status", "An error", "some data"); Response r2 = new Response("Status", "An error", 10); Response r3 = new Response("Status", "An error", new MyClass("data")); System.out.println(mapper.writeValueAsString(r1)); System.out.println(mapper.writeValueAsString(r2)); System.out.println(mapper.writeValueAsString(r3)); } @JsonAutoDetect(fieldVisibility=Visibility.ANY) public static class MyClass{ private String data; public MyClass(String data) { this.data = data; } } 

结果:

 {"status":"Status","error":"An error","data":"some data"} {"status":"Status","error":"An error","data":10} {"status":"Status","error":"An error","data":{"some.package.MyClass":{"data":"data"}}}