我想序列化一个对象,以便根据字段的类型对其中一个字段进行不同的命名。例如:
public class Response {
private Status status;
private String error;
private Object data;
[ getters, setters ]
}
在这里,我希望将字段data
序列化为data.getClass.getName()
,而不是总是有一个名为data
的字段,根据具体情况,它包含不同的类型。
我如何使用Jackson实现这样的技巧?
答案 0 :(得分:31)
使用自定义JsonSerializer
。
public class Response {
private String status;
private String error;
@JsonProperty("p")
@JsonSerialize(using = CustomSerializer.class)
private Object data;
// ...
}
public class CustomSerializer extends JsonSerializer<Object> {
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
花边持有者。如果要删除它,则必须为整个类编写自定义序列化程序,即JsonSerializer<Response>
。
答案 1 :(得分:16)
我使用@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<String, Object> any() {
//add the custom name here
//use full HashMap if you need more than one property
return Collections.singletonMap(data.getClass().getName(), data);
}
}
不需要包装器,不需要自定义序列化器。
答案 2 :(得分:3)
我自己的解决方案。
@Data
@EqualsAndHashCode
@ToString
@JsonSerialize(using = ElementsListBean.CustomSerializer.class)
public class ElementsListBean<T> {
public ElementsListBean()
{
}
public ElementsListBean(final String fieldName, final List<T> elements)
{
this.fieldName = fieldName;
this.elements = elements;
}
private String fieldName;
private List<T> elements;
public int length()
{
return (this.elements != null) ? this.elements.size() : 0;
}
private static class CustomSerializer extends JsonSerializer<Object> {
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();
}
}
}
}
答案 3 :(得分:2)
您可以使用注释JsonTypeInfo
,它可以准确地告诉Jackson,您不需要编写自定义序列化程序。有多种方式可以包含此信息,但针对您的具体问题,您可以使用As.WRAPPER_OBJECT
和Id.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"}}}