是否可以嵌套使用多个@JsonSubType
注释?
例如,设想以下类别:
@Data
@JsonSubTypeInfo(include=As.EXISTING_PROPERTY, property="species", use=Id.NAME, visible=true)
@JsonSubTypes({
@Type(name="Dog", value=Dog.class)
@Type(name="Cat", value=Cat.class)
})
public abstract class Animal {
private String name;
private String species;
}
@Data
@JsonSubTypeInfo(include=As.EXISTING_PROPERTY, property="breed", use=Id.NAME, visible=true)
@JsonSubTypes({
@Type(name="Labrador", value=Labrador.class)
@Type(name="Bulldog", value=Bulldog.class)
})
public abstract class Dog extends Animal {
private String breed;
}
@Data
public class Cat extends Animal {
private boolean lovesCatnip;
}
@Data
public class Labrador extends Dog {
private String color;
}
@Data
public class Bulldog extends Dog {
private String type; // "frenchy", "english", etc..
}
如果使用对象映射器,则可以成功地将Bulldog
映射到JSON,但是,当尝试读取生成的JSON并将其读回时,会出现如下错误:
Can not construct instance of com.example.Dog abstract types either need to be mapped to concrete types, have custom deserializer, or contain additional type information
是否有可能让Jackson进行此类分型?我需要为每个子类创建一个自定义反序列化器吗?
编辑:
我已经从原始帖子中稍微修改了上面的类。我添加了Cat
类,并从Dog
扩展了Animal
。
以下是可以使用ObjectMapper::writeValueAsString
创建的示例JSON:
{
"name": null,
"species": "Dog",
"breed": "Bulldog",
"type": "B-Dog"
}
答案 0 :(得分:1)
如果我使用@JsonTypeInfo
和与您类似的设置,则可以进行以下操作。也许您的问题出在反序列化代码中,所以请看以下内容:
public class MyTest {
@Test
public void test() throws IOException {
final Bulldog bulldog = new Bulldog();
bulldog.setBreed("Bulldog");
bulldog.setType("B-Dog");
final ObjectMapper om = new ObjectMapper();
final String json = om.writeValueAsString(bulldog);
final Dog deserialized = om.readValue(json, Dog.class);
assertTrue(deserialized instanceof Bulldog);
}
@JsonTypeInfo(include = As.EXISTING_PROPERTY, property = "species", use = Id.NAME, visible = true)
@JsonSubTypes({
@Type(name = "Dog", value = Dog.class),
@Type(name = "Cat", value = Cat.class)
})
public static abstract class Animal {
private String name;
private String species;
}
@JsonTypeInfo(include = As.EXISTING_PROPERTY, property = "breed", use = Id.NAME, visible = true)
@JsonSubTypes({
@Type(name = "Labrador", value = Labrador.class),
@Type(name = "Bulldog", value = Bulldog.class)
})
public static abstract class Dog {
private String breed;
public String getBreed() {
return breed;
}
public void setBreed(final String breed) {
this.breed = breed;
}
}
public static abstract class Cat {
private String name;
}
public static class Labrador extends Dog {
private String color;
public String getColor() {
return color;
}
public void setColor(final String color) {
this.color = color;
}
}
public static class Bulldog extends Dog {
private String type; // "frenchy", "english", etc..
public String getType() {
return type;
}
public void setType(final String type) {
this.type = type;
}
}
}
针对更新后的问题进行编辑:如果您可以为继承层次结构使用相同的属性(在以下代码中为隐藏属性“ @class”),那么它将起作用:
@Test
public void test() throws IOException {
final Bulldog bulldog = new Bulldog();
// bulldog.setSpecies("Dog");
// bulldog.setBreed("Bulldog");
bulldog.setType("B-Dog");
final ObjectMapper om = new ObjectMapper();
final String json = om.writeValueAsString(bulldog);
final Animal deserialized = om.readValue(json, Animal.class);
assertTrue(deserialized instanceof Bulldog);
}
@JsonTypeInfo(include = As.PROPERTY, use = Id.CLASS, visible = false)
@JsonSubTypes({
@Type(Dog.class),
@Type(Cat.class)
})
public static abstract class Animal {
}
@JsonTypeInfo(include = As.PROPERTY, use = Id.CLASS, visible = false)
@JsonSubTypes({
@Type(name = "Labrador", value = Labrador.class),
@Type(name = "Bulldog", value = Bulldog.class)
})
public static abstract class Dog
extends Animal {
}
如果要设置动物类型(例如,计算物种,品种等),也可以使用以下设置:
@Test
public void test() throws IOException {
final Bulldog bulldog = new Bulldog();
bulldog.setAnimalType("Bulldog");
// bulldog.setSpecies("Dog");
// bulldog.setBreed("Bulldog");
bulldog.setType("B-Dog");
final ObjectMapper om = new ObjectMapper();
final String json = om.writeValueAsString(bulldog);
System.out.println(json);
final Animal deserialized = om.readValue(json, Animal.class);
assertTrue(deserialized instanceof Bulldog);
}
@JsonTypeInfo(include = As.EXISTING_PROPERTY, property = "animalType", use = Id.NAME, visible = true)
@JsonSubTypes({
@Type(Dog.class)
})
public static abstract class Animal {
private String animalType;
public String getAnimalType() {
return animalType;
}
public void setAnimalType(final String animalType) {
this.animalType = animalType;
}
}
@JsonTypeInfo(include = As.EXISTING_PROPERTY, property = "animalType", use = Id.NAME, visible = true)
@JsonSubTypes({
@Type(value = Bulldog.class)
})
public static abstract class Dog
extends Animal {
}
@JsonTypeName("Bulldog")
public static class Bulldog extends Dog {
private String type; // "frenchy", "english", etc..
public String getType() {
return type;
}
public void setType(final String type) {
this.type = type;
}
}
答案 1 :(得分:1)
我能够解决此问题,以便将以下JSON转换为Bulldog
对象:
{
"species": "Dog",
"breed": "Bulldog",
"name": "Sparky",
"type": "English"
}
我使用以下代码执行此操作:
ObjectMapper om = new ObjectMapper();
om.addHandler(new DeserializationProblemHandler() {
@Override
public Object handleMissingInstantiator(DeserializationContext ctxt, Class<?> instClass, JsonParser p, String msg) throws IOException {
JsonNode o = p.readValueAsTree();
JsonNode copy = o.deepCopy();
JsonNode species = o.get("species");
if (species != null) {
Class<? extends Animal> clazz;
switch (species.asText()) {
case "Dog":
clazz = Dog.class;
break;
case "Cat":
clazz = Cat.class;
break;
default:
return NOT_HANDLED;
}
JsonParser parser = new TreeTraversingParser(copy, p.getCodec());
parser.nextToken(); // without this an error is thrown about missing "breed" type
return ctxt.readValue(parser, clazz);
}
return NOT_HANDLED;
}
});
我相信找到类型类型的类可能是更好的方法(我注意到cache
方法的输入之一中有一个handleMissingInstantiator
,其中包含所有相关类型,即可能可以用来根据名称查找类型,而不是像我现在那样对值进行硬编码。