多级@JsonTypeInfo 和@JsonSubTypes

Multiple level @JsonTypeInfo and @JsonSubTypes

我有 class 层次结构 A <- B <- C。我不想 class A 知道 C,所以我打算使用 A.type=" B" 表示它是 class B,然后 B.type2="C" 表示它是 class C.

@JsonTypeInfo(use = JsonTypeInfo.Id.NAME, include = JsonTypeInfo.As.EXISTING_PROPERTY, property = "type")
@JsonSubTypes({@JsonSubTypes.Type(value = B.class, name = "B")})
public abstract class A {

    private final String type;

    public A(String type) {
        this.type = type;
    }
}

@JsonTypeInfo(use = JsonTypeInfo.Id.NAME, include = JsonTypeInfo.As.EXISTING_PROPERTY, property = "type2")
@JsonSubTypes({@JsonSubTypes.Type(value = C.class, name = "C")})
public class B extends A {

    private final String type2;

    private final String propertyB;

    @JsonCreator
    public B(@JsonProperty("type2") String type2,
             @JsonProperty("propertyB") String propertyB) {
        super("B");
        this.type2 = type2;
        this.propertyB = propertyB;
    }
}

public class C extends B {

    private final String propertyC;

    @JsonCreator
    public C(@JsonProperty("propertyB") String propertyB,
             @JsonProperty("propertyC") String propertyC) {
        super("C", propertyB);
        this.propertyC = propertyC;
    }
}

当我将 C 的 JSON 读到 class A 时,实际的 Java 对象是 class B 而不是 C。

@Test
void whenReadCJsonToA_thenObjectIsInstanceOfC() throws JsonProcessingException {

    String json = "{\n" +
                  "  \"type\" : \"B\",\n" +
                  "  \"type2\" : \"C\",\n" +
                  "  \"propertyB\" : \"b\",\n" +
                  "  \"propertyC\" : \"c\"\n" +
                  "}";

    A obj = objectMapper.readValue(json, A.class);
    assertTrue(obj instanceof B, "obj is not instance of B");  // pass
    assertTrue(obj instanceof C, "obj is not instance of C");  // fail
}

使上述测试通过的一种方法是 writing custom deserializer,但如果 class 包含许多字段,则此解决方案很乏味。

有没有可能用更优雅的方式让上面的测试通过?我对级联@JsonTypeInfo 和@JsonSubTypes 的缩进是完全错误的吗?

我的maven项目可以在Github.

中找到

根据this Jackson issue,仅使用一个类型区分符属性 即可支持多级继承。在我的代码中,只保留 属性 type 并删除 属性 type2.

@JsonTypeInfo(use = JsonTypeInfo.Id.NAME, include = JsonTypeInfo.As.EXISTING_PROPERTY, property = "type")
@JsonSubTypes({@JsonSubTypes.Type(value = B.class, name = "B")})
public abstract class A {

    private final String type;

    public A(String type) {
        this.type = type;
    }
}

@JsonSubTypes({@JsonSubTypes.Type(value = C.class, name = "C")})
public class B extends A {

    private final String propertyB;

    @JsonCreator
    public B(@JsonProperty("propertyB") String propertyB) {
        super("B");
        this.propertyB = propertyB;
    }
}

public class C extends B {

    private final String propertyC;

    @JsonCreator
    public C(@JsonProperty("propertyB") String propertyB,
             @JsonProperty("propertyC") String propertyC) {
        super(propertyB);
        this.propertyC = propertyC;
    }
}

查看 this commit 中的完整代码。

This commit 使用枚举作为类型区分符 属性 类型。