我已经研究了实现对象类的最佳方式,继承并包含在一个对象中作为一个列表。
- 链接 1:How to configure Jackson to deserialize named types with default typing?
- 链接 2:https://github.com/FasterXML/jackson-docs/wiki/JacksonPolymorphicDeserialization
- 链接 3:http://www.davismol.net/2015/03/05/jackson-json-deserialize-a-list-of-objects-of-subclasses-of-an-abstract-class/
如果您知道处理该场景并提供正确答案的任何链接,我将很乐意将此视为重复并关闭此项目。 我有以下类(class):
动物园
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.fasterxml.jackson.annotation.JsonInclude;
import java.util.List;
import lombok.*;
@Getter
@Setter
@Builder(toBuilder = true)
@AllArgsConstructor
@NoArgsConstructor
@JsonIgnoreProperties(ignoreUnknown = true)
public class Zoo {
private String zooName;
private String zooLocation;
@JsonInclude(JsonInclude.Include.NON_EMPTY)
private @Singular List<Animal> animals;
}
动物
@Getter
@Setter
@Builder(builderMethodName = "parentBuilder")
@AllArgsConstructor
@NoArgsConstructor
@JsonIgnoreProperties(ignoreUnknown = true)
@JsonTypeInfo(
use = JsonTypeInfo.Id.NONE,
include = JsonTypeInfo.As.PROPERTY,
property = "type",
defaultImpl = Animal.class
)
@JsonSubTypes({
@JsonSubTypes.Type(value = Dog.class, name = "dog"),
@JsonSubTypes.Type(value = Elephant.class, name = "elephant")
})
public class Animal{
private String type;
private String nameTag;
}
狗
@Getter
@Setter
@AllArgsConstructor
@NoArgsConstructor
@EqualsAndHashCode(callSuper = false)
@JsonPropertyOrder({ "type", "nameTag"})
@JsonTypeName("dog")
public class Dog extends Animal{
private String barkingPower;
}
大象
@Getter
@Setter
@AllArgsConstructor
@NoArgsConstructor
@EqualsAndHashCode(callSuper = false)
@JsonPropertyOrder({ "type", "nameTag"})
public class Elephant extends Animal{
private String weight;
}
模型助手
public final class ModelHelper {
private static final ObjectMapper MAPPER = new ObjectMapper();
.configure(SerializationFeature.WRITE_NULL_MAP_VALUES, false)
.setSerializationInclusion(JsonInclude.Include.NON_NULL)
.enableDefaultTypingAsProperty(
ObjectMapper.DefaultTyping.JAVA_LANG_OBJECT, "type");
public static <T> T fromJson(@NotNull final String json, final Class<T> objectClass) {
try {
return MAPPER.readValue(json, objectClass);
} catch (Exception e) {
final String message = String.format("Error de-serialising JSON '%s' to object of %s",
json, objectClass.getName());
log.warn(message, e);
return null;
}
}
}
我正在使用 ModelHelper 将 JSON 反序列化为一个对象:
String json = "{\"zooName\":\"Sydney Zoo\",\"zooLocation\":\"Sydney\",\"animals\":[{\"type\":\"dog\",\"nameTag\":\"Dog1\",\"barkingPower\":\"loud\"},{\"type\":\"elephant\",\"nameTag\":\"Elephant1\",\"weight\":\"heavy\"}]}";
mapper.readValue(json, Zoo.class);
当前反序列化Zoo
只返回 Animal
属性而不是 Dog
或 Elephant
属性。
我的问题是:
- 我可以收集到的是反序列化不起作用,因为
Zoo
中的 getter对于List<Animal>
是基类类型,反序列化无法计算出如何创建Dog
或Elephant
,并根据签名生成Animal
.但我会认为通过输入JsonTypeName
和JsonSubTypes
注释我已经标记了相关的子类。是这样吗? - 是否
Animal
类必须定义为abstract
这行得通吗? - 使它起作用的唯一方法是为类 Zoo 实现自定义反序列化吗?这是一个很好的例子吗:https://www.sghill.net/how-do-i-write-a-jackson-json-serializer-deserializer.html ?
如果代码不清楚,请告诉我,我会修复它。
最佳答案
您混合了大约 3 种方法来反序列化多态类型,考虑到 Jackson 经历了多少次迭代,这并不奇怪。
您将 Animal
标记为
@JsonTypeInfo(use = JsonTypeInfo.Id.NONE, include = JsonTypeInfo.As.PROPERTY, property = "type")
这告诉 Jackson 2 自相矛盾的事情:
include
和property
字段声明您要使用名为type
的属性use
字段声明不应使用任何 ID 机制,从而有效地忽略您的类型信息。
最简单的“修复”是改用 JsonTypeInfo.Id.NAME
,这将输出(我添加了 Lombok 的 @ToString
):
Zoo(zooName=Sydney Zoo, zooLocation=Sydney, animals=[Dog(barkingPower=loud), Elephant(weight=heavy)])
现在你有了正确的类型识别。请注意,在这种情况下,不需要子类上的 @JsonTypeName
注释,因为您已经指定了一种解析类型的方法:@JsonTypeInfo
告诉反序列化器寻找一个type
属性和 @JsonSubTypes
告诉它将它的值(name
属性)映射到它的类(value
属性(property))。您也不需要指定任何 ObjectMapper
配置。
@JsonTypeName
是干什么用的?它替换了 @JsonSubTypes
中的 name
:
@JsonSubTypes({
@JsonSubTypes.Type(value = Dog.class, name = "dog"),
@JsonSubTypes.Type(value = Elephant.class) // no name
})
public class Animal { ... }
public class Dog extends Animal { ... }
@JsonTypeName("elephant") // name is here
public class Elephant extends Animal { ... }
这很有用,例如,如果您无权访问父类(super class)。
另一种无需注释即可完成所有这些操作的方法(同样,如果您无权访问类,这很有用)是配置映射器。您可以使用 @JsonSubTypes
而不是
MAPPER.registerSubtypes(new NamedType(Dog.class, "dog"), new NamedType(Elephant.class, "elephant"));
此处包括类型名称-类映射。
很可能有更多混合配置的方法,但这充分说明了这一点并回答了您的问题。
问题是我无法将父类(super class)属性与子类属性一起反序列化。它是一个或另一个。也许自定义反序列化器确实可以解决这个问题。
关于java - 如何配置 Jackson 以反序列化包含在基类上使用 @JsonTypeInfo 和 JsonSubTypes 注释的命名类型的列表?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/47372486/