杰克逊动态属性名称

2022-09-01 07:14:58

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

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

在这里,我希望将字段序列化为类似的东西,而不是总是调用一个包含不同类型的字段,具体取决于情况。datadata.getClass.getName()data

我如何使用杰克逊实现这样的技巧?


答案 1

我有一个使用注释的更简单的解决方案,它就像一个魅力。@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

使用自定义 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"}}

我使用了包装器对象的名称,因为它仅用作花边夹。如果要删除它,则必须为整个类编写自定义序列化程序,即 .ppJsonSerializer<Response>