使用杰克逊

时间:2019-10-06 18:37:55

标签: java json jackson

我正在尝试将此json数据反序列化为对象列表:

[{
  "a": {
    "commonField": 1,
    "aField": "AAA"
  }
}, {
  "b": {
    "commonField": 2,
    "bField": "BBB"
  }
}]

每个对象可以是具有公共字段和唯一字段的几种类型之一。有关对象确切形状的信息存储在json中,作为包装对象中的键。

我为每个已知形状(一组唯一字段)创建了对应的类,扩展了包含所有公共字段的类。另外,我在类中添加了Jackson批注以启用多态反序列化。简化的结果类如下所示:

@JsonTypeInfo(use = Id.NAME, include = As.WRAPPER_OBJECT, property = "type")
@JsonSubTypes({
  @JsonSubTypes.Type(KeyBasedSubTypeA.class),
  @JsonSubTypes.Type(KeyBasedSubTypeB.class)
})
public abstract class KeyBasedSuperType {
  public String type;
  public int commonField;
}

@JsonTypeName("a")
public class KeyBasedSubTypeA extends KeyBasedSuperType {
  public String aField;
}

@JsonTypeName("b")
public class KeyBasedSubTypeB extends KeyBasedSuperType {
  public String bField;
}

通过此设置,杰克逊几乎可以完美地工作。它可以在反序列化期间选择正确的子类型,并填充所有字段,包括公共字段和唯一字段。但是,type字段不会由Jackson进行更新,用于选择子类型的键值不会存储在任何地方。换句话说,数据反序列化为以下结构:

[KeyBasedSubTypeA { type=null; commonField=1; aField=AAA },
 KeyBasedSubTypeB { type=null; commonField=2; bField=BBB }]

注意type字段的值为空。因此,问题是-我怎样才能让Jackson储存用于选择结果对象中某个地方的子类型的包装器的键?

这是我对该过程的JUnit测试

public class PolymorphicTest {
  private static ObjectMapper mapper;

  @BeforeClass
  public static void init() {
    mapper = new ObjectMapper();
  }

  @Test
  public void testKeyDenominator() throws IOException {
    TypeReference<List<KeyBasedSuperType>> dataShape =
        new TypeReference<List<KeyBasedSuperType>>() {};
    List<KeyBasedSuperType> result = mapper.readValue(
        PolymorphicTest.class.getResourceAsStream("polymorphic-key.json"), dataShape);
    assertEquals(2, result.size());
    assertEquals(KeyBasedSubTypeA.class, result.get(0).getClass());
    assertEquals(KeyBasedSubTypeB.class, result.get(1).getClass());
    assertEquals(1, result.get(0).commonField);
    assertEquals(2, result.get(1).commonField);
    assertEquals("a", result.get(0).type); // <---- this line fails
    assertEquals("b", result.get(1).type); // <---- this line fails
    assertEquals("AAA", ((KeyBasedSubTypeA) result.get(0)).aField);
    assertEquals("BBB", ((KeyBasedSubTypeB) result.get(1)).bField);
  }

}

1 个答案:

答案 0 :(得分:1)

解决方案实际上非常接近,只是错过了前进的一小步。 @JsonTypeInfo(visible=true)必须使Jackson处理类型信息成为常规属性。

@JsonTypeInfo(use = Id.NAME, include = As.WRAPPER_OBJECT, property = "type", visible = true)
@JsonSubTypes({
  @JsonSubTypes.Type(KeyBasedSubTypeA.class),
  @JsonSubTypes.Type(KeyBasedSubTypeB.class)
})
public abstract class KeyBasedSuperType {
  public String type;
  public int commonField;
}