如何动态反序列化多次出现T的泛型类

时间:2017-10-10 08:49:16

标签: java json generics serialization jackson

我正在尝试序列化泛型类的实例,同时保留泛型类型,因此我以后可以反序列化它,而不必手动指定泛型类型。

我理解反序列化通用对象的常用方法是使用类型引用或JavaType这样的对象:

ObjectMapper om = new ObjectMapper();
ObjectReader or = om.reader();
ObjectWriter ow = om.writer();

String json = "[1, 2, 3]"
JavaType listType = or.getTypeFactory()
                      .constructParametricType(List.class, Integer.class);
List<Integer> integers = or.forType(listType).readValue(json)

但我事先并不知道泛型类型(在这种情况下为Integer),因此我无法做到这一点。

我也明白,由于擦除,我必须以某种方式在序列化JSON中包含类型信息。这可以使用@JsonTypeInfo注释完成:

class Pojo<T> {
    @JsonTypeInfo(use = JsonTypeInfo.Id.CLASS)
    public T value;
}

但是,如果在其他各个地方使用类型T,这会很快变得臃肿。请考虑以下示例:

class Pojo<T> {
    @JsonTypeInfo(use = JsonTypeInfo.Id.CLASS)
    public T value;
    @JsonTypeInfo(use = JsonTypeInfo.Id.CLASS)
    public List<T> otherStuff;
    // constructor
}
// ...
Pojo<BigDecimal> pojo = new Pojo<>(
    BigDecimal.valueOf(42),
    Lists.newArrayList(BigDecimal.valueOf(14), BigDecimal.valueOf(23))
);
final String json = ow.writeValueAsString(pojo);
System.out.println(json);

产生以下结果:

{
    "value": ["java.math.BigDecimal", 42],
    "otherStuff":[
        ["java.math.BigDecimal", 14],
        ["java.math.BigDecimal", 23]
    ]
}

在每种对象中重复类型BigDecimal。这是不必要的,因为无论如何,T的所有出现的类型都是相同的(除了我想的一些多态情况)。

如果@JsonTypeInfo省略otherStuff注释,则杰克逊无法将otherStuff的内容类型从value类型中推迟。在此示例中,即使otherStuff的类型为List<Integer>,它也会将value反序列化为BigDecimal

如何序列化泛型类的实例,以便以后可以安全地反序列化它们并保留泛型参数?

1 个答案:

答案 0 :(得分:0)

类型信息确实需要包含在序列化的json字符串中,但只能包含一次。我所知道的最简单的方法是使用@JsonCreator注释编写自定义创建者方法,分两步执行反序列化:

  1. 让杰克逊反序列化所有非通用字段,以及一个包含类型信息的通用字段。将其他通用字段捕获为原始JsonNode,以便可以手动反序列化。
  2. 在运行时获取该类型信息以反序列化其余字段。
  3. 对于上面的例子,这将是这样的(省略了异常处理代码):

    class Pojo<T> {
        @JsonTypeInfo(use = JsonTypeInfo.Id.CLASS)
        public T value;
        public List<T> otherStuff;
        // constructor
    
        @JsonCreator
        public static <T> Pojo<T> jsonCreator(
                @JsonProperty("value") T value,
                @JsonProperty("otherStuff") JsonNode otherStuffRaw) {
            JavaType listType = or.getTypeFactory()
                                  .constructParametricType(List.class, value.getClass());
            return new Pojo<T>(
                value,
                or.forType(listType).readValue(otherStuffRaw)
            );
        }
    }
    

    然而,这允许value成为T的子类,这可能会产生意外结果。如果这是一个问题,另一种方法可能是使用Class<T>来保留T的确切类型。这也可以屏蔽value可能null