java将多态对象列表反序列化为对象字段
我的REST端点返回包含对象字段的响应对象。序列化一切正常,但当我开始为这个API编写客户端时,我遇到了反序列化问题。我根据一些关于Jackson多态序列化的问题/文章编写了这个示例代码。这说明了这个问题
@Data
abstract class Animal {
String name;
}
@Data
class Dog extends Animal {
boolean canBark;
}
@Data
class Cat extends Animal {
boolean canMeow;
}
@Data
public class Zoo {
private Object animals;
}
@JsonTypeInfo(use = JsonTypeInfo.Id.NAME, include = JsonTypeInfo.As.PROPERTY, property = "name", visible = true)
@JsonSubTypes({
@JsonSubTypes.Type(name = "dog", value = Dog.class),
@JsonSubTypes.Type(name = "cat", value = Cat.class)
})
public class Mixin {
}
public class Main {
private static final String JSON_STRING = "{\n"
+ " \"animals\": [\n"
+ " {\"name\": \"dog\"},\n"
+ " {\"name\": \"cat\"}\n"
+ " ]\n"
+ "}";
public static void main(String[] args) throws IOException {
ObjectMapper objectMapper = Jackson.newObjectMapper()
.setPropertyNamingStrategy(PropertyNamingStrategy.SNAKE_CASE)
.setDefaultPropertyInclusion(Include.NON_NULL);
objectMapper.addMixIn(Animal.class, Mixin.class);
Zoo zoo = objectMapper.readValue(JSON_STRING, Zoo.class);
for (Object animal : (Collection) zoo.getAnimals()) {
System.out.println(animal.getClass());
}
}
}
我对输出的期望(以及我对List<Animals>
作为动物园#动物类型的期望):
class jackson.poly.Dog
class jackson.poly.Cat
我现在对Object的看法是:
class java.util.LinkedHashMap
class java.util.LinkedHashMap
但我需要反序列化除动物列表之外的其他类型的对象。救命啊
# 1 楼答案
我向所有参与的人表示诚挚的感谢。我决定采用一种有点内省的方法,并编写了自定义反序列化程序,查看列表中第一个对象的内部,以确定是否需要将其反序列化为列表
也许有些支票是多余的。它是有效的,这才是最重要的:)。 该奖项授予斯蒂芬施莱希特《最鼓舞人心的答案》一书的作者。再次感谢
# 2 楼答案
如果知道要转换为的类型,可以使用
ObjectMapper
的方法convertValue
可以转换单个值或整个列表
考虑下面的例子:
对于您的评论,如果您不确定需要处理的实际类型,一个有用的选项是启用Jackson的默认类型(使用required precautions)
你可以这样做:
您可以做的另一件事是为包含任意信息的字段定义一个自定义
Deserializer
您可以重用一些代码,而不是从头开始创建一个
当您将属性定义为
Object
时,Jackson会将类com.fasterxml.jackson.databind.deser.std.UntypedObjectDeserializer
的一个实例指定为它的Deserializer
在您的例子中,这个类将首先处理数组,因此我们获得一个
List
作为主要的反序列化结果这个
List
将由类LinkedHashMap
的实例组成。为什么?因为在它们的实现中,对于数组中的每个JSON对象,它们都会生成一个带有键的LinkedHashMap
,JSON对象属性名称按它们出现的顺序,以及一个带有JSON对象属性值的String[]
这个过程由
mapObject
方法处理。如果您有某种类型的字段,允许您标识正在处理的Object
的类型,那么您可以定义一个新的Deserializer
,它扩展UntypedObjectDeserializer
并覆盖mapObject
方法,从其父级返回的Map
创建实际的、具体的对象,只需应用JavaBean内省和属性设置器最后一种方法也可以用
Converter
实现。它们实际上非常适合这两个阶段的过程。其背后的想法与上一段中描述的相同:您将收到List
个Map
,如果您能够识别具体的类型,您只需要根据收到的信息构建相应的实例。您甚至可以在最后一个转换步骤中使用ObjectMapper