Jackson JSON库:如何实例化包含抽象字段的类

48

我想将一个JSON字符串转换为Java对象,但这个对象的类包含抽象字段,Jackson无法实例化它们,也不能产生对象。有没有最简单的方法告诉Jackson关于某个抽象类的默认实现?

setDefault(AbstractAnimal.class, Cat.class);

或者根据JSON属性名称决定实现类,例如对于JSON对象:

{
    ...
    cat: {...}
    ...
}
我会简单写出:
setImpl("cat", Cat.class);


我知道在Jackson中可以将类信息嵌入JSON中,但我不想让我使用的JSON格式变得复杂。我想通过设置默认实现类或属性名称('cat')来决定要使用哪个类 - 就像在XStream库中编写的那样:

xStream.alias("cat", Cat.class);

有没有一种方法可以这样做,特别是在一行内,或者需要更多的代码?


Java 中不存在“抽象字段”这样的东西。 - user207421
1
我想表达的是: class C { Animal animal; } 我想实例化C,其中Animal是抽象的,我想将一个继承自Animal的Cat放入这个字段中。 - Marcin
所以没有问题。变量可以是抽象类型,没有任何规定。 - user207421
可以在JSON中嵌入类信息:@JsonTypeInfo(use = JsonTypeInfo.Id.CLASS, include = JsonTypeInfo.As.PROPERTY, property = "type")。请参见https://dev59.com/5VwY5IYBdhLWcg3wU2TA#32777371中的评论。 - koppor
4个回答

65

有多种方法;在1.8版本之前,最简单的方法可能是执行以下操作:

@JsonDeserialize(as=Cat.class)
public abstract class AbstractAnimal { ... }

关于基于属性进行决策,最好使用 @JsonTypeInfo 来实现,它会自动嵌入(写入时)和使用类型信息。

有多种类型信息(类名、逻辑类型名),以及包含机制(作为包含属性、作为包装数组、作为包装对象)。此页面:https://github.com/FasterXML/jackson-docs/wiki/JacksonPolymorphicDeserialization 解释了一些概念。


8
注释也相当缺乏灵活性,因为我无法动态决定使用哪种实现。 - Marcin
1
关于修改类,这就是Jackson混合注释的作用。但是正确的做法是,此注释仅适用于只有一个实现类型的情况。Jackson 1.8将具有功能,允许简单的抽象类型->实现类型映射,如果有必要的话。 - StaxMan
2
混合注释起了作用,感谢指出正确的方向。 - Marcin
1
这允许只指定一个类吗?如果有10个类怎么办? - Danubian Sailor
1
链接的维基已经失效 - 新的URL似乎是https://github.com/FasterXML/jackson-docs/wiki/JacksonPolymorphicDeserialization - Philipp Nowak
显示剩余2条评论

5

这里有一个详细的答案,包含一个非常清晰的例子:https://dev59.com/Wl0a5IYBdhLWcg3waYCM#30386694

Jackson将之称为多态反序列化。

这对于我的问题确实很有帮助。我有一个抽象类,我需要将它保存到数据库中,并将其解组成一个类的具体实例(可以理解)。

它会向您展示如何正确地注释父抽象类以及如何在运行时教jackson从可用的子类候选项中选择。


5

如果您不想在JSON中添加额外的字段,也不想在类上使用注解,可以编写一个非常简单的模块和反序列化程序,该程序使用您想要的默认子类。由于一些样板代码,它不止一行,但仍然相对简单。

class AnimalDeserializer extends StdDeserializer<Animal> {
    public AnimalDeserializer() {
        super(Animal.class);
    }

    public Animal deserialize(JsonParser jsonParser, DeserializationContext context) throws IOException {
        return jsonParser.readValueAs(Cat.class);
    }
}

class AnimalModule extends SimpleModule {
    {
        addDeserializer(Animal.class, new AnimalDeserializer());
    }
}

然后将此模块注册到ObjectMapper中,就这样(Zoo是具有Animal字段的容器类)。

ObjectMapper objectMapper = new ObjectMapper();
objectMapper.registerModule(new AnimalModule());
return objectMapper.readValue(json, Zoo.class);

谢谢这个。救了我的一天。我有多个抽象类的实现,所以我不得不在自定义反序列化器中放置一个开关案例。 - आनंद
模块的样板可以简化如下:objectMapper.registerModule(new SimpleModule().addDeserializer(Animal.class, new AnimalDeserializer())); - Alessandro S.

3

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接