我的 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>
作为 Zoo#animals 类型的期望):
class jackson.poly.Dog
class jackson.poly.Cat
我现在拥有的对象:
class java.util.LinkedHashMap
class java.util.LinkedHashMap
但是除了动物列表之外,我还需要反序列化其他类型的对象。请帮忙!
最佳答案
不幸的是,反序列化器接受来自字段声明类型的类型。因此,如果字段是 Object
,则它不会反序列化为任何内容,并将其保留为 Map
。对此没有简单的解决方案,因为反序列化没有关于它应该是什么类型的信息。一种解决方案是自定义映射器,就像其中一个答案一样,或者您可以使用自定义 TaggedObject
类而不是我在类似用例中使用的 Object
:
public class TaggedObject {
@Expose
private String type;
@Expose
private Object value;
@Expose
private Pair<TaggedObject, TaggedObject> pairValue;
@SuppressWarnings("unchecked")
public TaggedObject(Object v) {
this.type = getTypeOf(v);
if (v instanceof Pair) {
this.pairValue = tagPair((Pair<Object, Object>) v);
this.value = null;
} else if ("long".equals(type)){
this.value = "" + v;
this.pairValue = null;
} else {
this.value = v;
this.pairValue = null;
}
}
private static Pair<TaggedObject, TaggedObject> tagPair(Pair<Object, Object> v) {
return new Pair<TaggedObject, TaggedObject>(TaggedObject.tag(v.first), TaggedObject.tag(v.second));
}
private static String getTypeOf(Object v) {
Class<?> cls = v.getClass();
if (cls.equals(Double.class))
return "double";
if (cls.equals(Float.class))
return "float";
if (cls.equals(Integer.class))
return "integer";
if (cls.equals(Long.class))
return "long";
if (cls.equals(Byte.class))
return "byte";
if (cls.equals(Boolean.class))
return "boolean";
if (cls.equals(Short.class))
return "short";
if (cls.equals(String.class))
return "string";
if (cls.equals(Pair.class))
return "pair";
return "";
}
public static TaggedObject tag(Object v) {
if (v == null)
return null;
return new TaggedObject(v);
}
public Object get() {
if (type.equals("pair"))
return new Pair<Object, Object>(
untag(pairValue.first),
untag(pairValue.second)
);
return getAsType(value, type);
}
private static Object getAsType(Object value, String type) {
switch (type) {
case "string" : return value.toString();
case "double" : return value;
case "float" : return ((Double)value).doubleValue();
case "integer": return ((Double)value).intValue();
case "long" : {
if (value instanceof Double)
return ((Double)value).longValue();
else
return Long.parseLong((String) value);
}
case "byte" : return ((Double)value).byteValue();
case "short" : return ((Double)value).shortValue();
case "boolean": return value;
}
return null;
}
public static Object untag(TaggedObject object) {
if (object != null)
return object.get();
return null;
}
}
这是针对 google gson 的(这就是为什么有 @Expose
注释的原因)但应该可以与 jackson 一起正常工作。我没有包含 Pair
类,但您当然可以根据签名创建自己的类或省略它(我需要序列化对)。
关于java - 将多态对象列表反序列化为对象字段,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/63339255/