杰克逊动态属性名称

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了杰克逊动态属性名称相关的知识,希望对你有一定的参考价值。

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

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<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作为包装器对象,因为它只是用作placeholder。如果要删除它,则必须为整个类编写自定义序列化程序,即JsonSerializer<Response>

另一答案

我使用@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);
    }
}

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

另一答案

我自己的解决方案

@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();
            }
        }
    }
}
另一答案

您可以使用注释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"}}}

以上是关于杰克逊动态属性名称的主要内容,如果未能解决你的问题,请参考以下文章

如何动态忽略杰克逊序列化的属性

我如何告诉杰克逊忽略我无法控制源代码的属性?

为动态创建的 Android 片段提供自定义属性值

Android - 导航抽屉 - 与动态菜单项重叠的片段

动态SQL基础概念复习(Javaweb作业5)

android 杰克逊 @JsonIgnore 属性