我有一个Animal
类,它接受通用类型参数T
,如下所示:
public class Animal<T> {
private String type;
private T details;
// getters and setters
}
type参数可以是Dog
或Cat
。
public class Dog {
private String name;
private boolean goodBoy;
// no-arg, all-args constructors, getters and setters
}
public class Cat {
private String name;
private boolean naughty;
// no-arg, all-args constructors, getters and setters
}
我正在尝试反序列化以下JSON
。
{
"type": "dog",
"details": {
"name": "Marley",
"goodBoy": true
}
}
但是,当我反序列化时,字段详细信息总是反序列化为LinkedHashMap
而不是类的特定实现。
public static void main(String[] args) throws IOException {
ObjectMapper mapper = new ObjectMapper();
Animal<Dog> dog = new Animal<>();
dog.setType("dog");
dog.setDetails(new Dog("Marley", true));
String dogJson = mapper.writeValueAsString(dog);
Animal dogDeserialized = mapper.readValue(dogJson, Animal.class);
// dogDeserialized's details is LinkedHashMap
}
我不能更改上述类,因此不能在字段上使用注释。有没有一种方法可以指定ObjectMapper
可以将details
字段反序列化为的类的列表?
请注意,对于各个type
或Dog
类,Cat
字段的值设置为“ dog”或“ cat”。
答案 0 :(得分:1)
使用TypeReference
指定要反序列化的类型
Animal<Dog> dogDeserialized = mapper.readValue(
dogJson, new TypeReference<Animal<Dog>>() {});
或者代替Dog
和Cat
,您只能拥有一个具有所有属性的类
public class AnimalDetails {
private String name;
private Boolean goodBoy;
private Boolean naughty;
}
并设置ObjectMapper以忽略JSON中的未知属性:
ObjectMapper mapper = new ObjectMapper()
.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
因此,如果它是Dog
,则类naughty
将是null
;如果是Cat
,则类goodBoy
将是null
答案 1 :(得分:0)
因为您无法更改POJO
模型,所以需要实现自定义反序列化器并手动处理类型。自定义反序列化器可能如下所示:
class AnimalJsonDeserializer extends JsonDeserializer<Animal> {
private Map<String, Class> availableTypes = new HashMap<>();
public AnimalJsonDeserializer() {
availableTypes.put("cat", Cat.class);
availableTypes.put("dog", Dog.class);
}
@Override
public Animal deserialize(JsonParser parser, DeserializationContext ctxt) throws IOException {
ObjectNode root = parser.readValueAsTree();
JsonNode type = getProperty(parser, root, "type");
Animal<Object> animal = new Animal<>();
animal.setType(type.asText());
Class<?> pojoClass = availableTypes.get(animal.getType());
if (pojoClass == null) {
throw new JsonMappingException(parser, "Class is not found for " + animal.getType());
}
JsonNode details = getProperty(parser, root, "details");
animal.setDetails(parser.getCodec().treeToValue(details, pojoClass));
return animal;
}
private JsonNode getProperty(JsonParser parser, ObjectNode root, String property) throws JsonMappingException {
JsonNode value = root.get(property);
if (value.isMissingNode() || value.isNull()) {
throw new JsonMappingException(parser, "No " + property + " field!");
}
return value;
}
}
我们需要使用SimpleModule
类,并为Animal
类注册反序列化器。用法示例:
import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.databind.DeserializationContext;
import com.fasterxml.jackson.databind.JsonDeserializer;
import com.fasterxml.jackson.databind.JsonMappingException;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.module.SimpleModule;
import com.fasterxml.jackson.databind.node.ObjectNode;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
public class JsonApp {
public static void main(String[] args) throws Exception {
SimpleModule animalModule = new SimpleModule();
animalModule.addDeserializer(Animal.class, new AnimalJsonDeserializer());
ObjectMapper mapper = new ObjectMapper();
mapper.registerModule(animalModule);
Animal<Dog> dog = new Animal<>();
dog.setType("dog");
dog.setDetails(new Dog("Marley", true));
Animal<Cat> cat = new Animal<>();
cat.setType("cat");
cat.setDetails(new Cat("Tom", false));
Animal<Dog> husky = new Animal<>();
husky.setType("husky");
husky.setDetails(new Dog("Sib", true));
for (Animal animal : new Animal[]{dog, cat, husky}) {
String json = mapper.writeValueAsString(animal);
System.out.println("JSON: " + json);
System.out.println("Deserialized: " + mapper.readValue(json, Animal.class));
System.out.println();
}
}
}
上面的代码显示:
JSON: {"type":"dog","details":{"name":"Marley","goodBoy":true}}
Deserialized: Animal{type='dog', details=Dog{name='Marley', goodBoy=true}}
JSON: {"type":"cat","details":{"name":"Tom","naughty":false}}
Deserialized: Animal{type='cat', details=Cat{name='Tom', naughty=false}}
JSON: {"type":"husky","details":{"name":"Sib","goodBoy":true}}
Exception in thread "main" com.fasterxml.jackson.databind.JsonMappingException: Class is not found for husky
at [Source: (String)"{"type":"husky","details":{"name":"Sib","goodBoy":true}}"; line: 1, column: 56]
at AnimalJsonDeserializer.deserialize(JsonApp.java:69)
at AnimalJsonDeserializer.deserialize(JsonApp.java:50)
at com.fasterxml.jackson.databind.ObjectMapper._readMapAndClose(ObjectMapper.java:4013)
at com.fasterxml.jackson.databind.ObjectMapper.readValue(ObjectMapper.java:3004)
at com.celoxity.JsonApp.main(JsonApp.java:44)
如果您不能更改源类,则可以始终使用Mix-in
功能,该功能允许使用类似的方法创建新接口并适当地注释所有必需的类。对于您来说,我们还需要删除type
将自动处理的Jackson
属性。更改后,您的示例如下所示:
import com.fasterxml.jackson.annotation.JsonSubTypes;
import com.fasterxml.jackson.annotation.JsonTypeInfo;
import com.fasterxml.jackson.databind.ObjectMapper;
public class JsonApp {
public static void main(String[] args) throws Exception {
ObjectMapper mapper = new ObjectMapper();
mapper.addMixIn(Animal.class, AnimalMixIn.class);
Animal<Dog> dog = new Animal<>();
dog.setDetails(new Dog("Marley", true));
String dogJson = mapper.writeValueAsString(dog);
System.out.println(dogJson);
Animal dogDeserialized = mapper.readValue(dogJson, Animal.class);
System.out.println(dogDeserialized);
}
}
interface AnimalMixIn {
@JsonTypeInfo(use = JsonTypeInfo.Id.NAME, property = "type", include = JsonTypeInfo.As.EXTERNAL_PROPERTY)
@JsonSubTypes(value = {
@JsonSubTypes.Type(value = Dog.class, name = "dog"),
@JsonSubTypes.Type(value = Cat.class, name = "cat")})
Object getDetails();
}
class Animal<T> {
private T details;
public T getDetails() {
return details;
}
public void setDetails(T details) {
this.details = details;
}
@Override
public String toString() {
return "Animal{details=" + details + '}';
}
}
Cat
和Dogs
类保持不变。上面的代码打印出来:
{"details":{"name":"Marley","goodBoy":true},"type":"dog"}
Animal{details=Dog{name='Marley', goodBoy=true}}
另请参阅: