我有一个来自Web服务的结果,它返回一个布尔值或一个单例映射,例如。
布尔结果:
{
id: 24428,
rated: false
}
地图结果:
{
id: 78,
rated: {
value: 10
}
}
我个人可以轻松地映射这两个,但我如何一般地做到这一点?
基本上我想把它映射到类:
public class Rating {
private int id;
private int rated;
...
public void setRated(?) {
// if value == false, set rated = -1;
// else decode "value" as rated
}
}
所有多态示例都使用@JsonTypeInfo
根据数据中的属性进行映射,但在这种情况下我没有该选项。
<小时/> 的修改
@JsonProperty("rated")
public void setRating(JsonNode ratedNode) {
JsonNode valueNode = ratedNode.get("value");
// if the node doesn't exist then it's the boolean value
if (valueNode == null) {
// Use a default value
this.rating = -1;
} else {
// Convert the value to an integer
this.rating = valueNode.asInt();
}
}
答案 0 :(得分:14)
不不不。您不必编写自定义反序列化程序。首先使用“无类型”映射:
public class Response {
public long id;
public Object rated;
}
// OR
public class Response {
public long id;
public JsonNode rated;
}
Response r = mapper.readValue(source, Response.class);
为“评级”提供Boolean
或java.util.Map
的值(使用第一种方法);或者在第二种情况下JsonNode
。
由此,您可以按原样访问数据,或者更有趣的是,可以转换为实际值:
if (r.rated instanced Boolean) {
// handle that
} else {
ActualRated actual = mapper.convertValue(r.rated, ActualRated.class);
}
// or, if you used JsonNode, use "mapper.treeToValue(ActualRated.class)
还有其他类型的方法 - 使用创建者“ActualRated(boolean)”,让实例从标量中的POJO,或构造。但我认为上面应该有效。
答案 1 :(得分:2)
您必须编写自己的反序列化程序。它看起来像这样:
@SuppressWarnings("unchecked")
class RatingJsonDeserializer extends JsonDeserializer<Rating> {
@Override
public Rating deserialize(JsonParser jp, DeserializationContext ctxt) throws IOException, JsonProcessingException {
Map<String, Object> map = jp.readValueAs(Map.class);
Rating rating = new Rating();
rating.setId(getInt(map, "id"));
rating.setRated(getRated(map));
return rating;
}
private int getInt(Map<String, Object> map, String propertyName) {
Object object = map.get(propertyName);
if (object instanceof Number) {
return ((Number) object).intValue();
}
return 0;
}
private int getRated(Map<String, Object> map) {
Object object = map.get("rated");
if (object instanceof Boolean) {
if (((Boolean) object).booleanValue()) {
return 0; // or throw exception
}
return -1;
}
if (object instanceof Map) {
return getInt(((Map<String, Object>) object), "value");
}
return 0;
}
}
现在你必须告诉杰克逊将这个解串器用于Rating
类:
@JsonDeserialize(using = RatingJsonDeserializer.class)
class Rating {
...
}
简单用法:
ObjectMapper objectMapper = new ObjectMapper();
System.out.println(objectMapper.readValue(json, Rating.class));
以上程序打印:
Rating [id=78, rated=10]
表示JSON:
{
"id": 78,
"rated": {
"value": 10
}
}
并打印:
Rating [id=78, rated=-1]
表示JSON:
{
"id": 78,
"rated": false
}
答案 2 :(得分:1)
我问了一个类似的问题 - JSON POJO consumer of polymorphic objects
您必须编写自己的deserialiser
,以便在反序列化过程中查看并根据数据决定要执行的操作。
可能有其他更简单的方法,但这种方法对我来说效果很好。
答案 3 :(得分:1)
我找到了一篇有关该主题的好文章:http://programmerbruce.blogspot.com/2011/05/deserialize-json-with-jackson-into.html
我认为解析为对象的方法可能会出现问题,因为发送时会发送一个字符串。我不确定这是一个实际问题,但听起来像是一些可能的意外行为。 示例5和6显示可以为此使用继承。
示例:
示例6:不带类型元素的简单反序列化到具有多态集合的容器对象
一些现实世界中的JSON API具有多态类型成员,但不包含类型元素(与前面示例中的JSON不同)。将这些源反序列化为多态集合要复杂得多。以下是一种相对简单的解决方案。 (此示例包括随后将反序列化的Java结构序列化回输入JSON,但是序列化相对没有意思。)
// input and output: // { // "animals": // [ // {"name":"Spike","breed":"mutt","leash_color":"red"}, // {"name":"Fluffy","favorite_toy":"spider ring"}, // {"name":"Baldy","wing_span":"6 feet", // "preferred_food":"wild salmon"} // ] // } import java.io.File; import java.io.IOException; import java.util.Collection; import java.util.HashMap; import java.util.Iterator; import java.util.Map; import java.util.Map.Entry; import org.codehaus.jackson.JsonNode; import org.codehaus.jackson.JsonParser; import org.codehaus.jackson.JsonProcessingException; import org.codehaus.jackson.Version; import org.codehaus.jackson.map.DeserializationContext; import org.codehaus.jackson.map.ObjectMapper; import org.codehaus.jackson.map.deser.StdDeserializer; import org.codehaus.jackson.map.module.SimpleModule; import org.codehaus.jackson.node.ObjectNode; import fubar.CamelCaseNamingStrategy; public class Foo { public static void main(String[] args) throws Exception { AnimalDeserializer deserializer = new AnimalDeserializer(); deserializer.registerAnimal("leash_color", Dog.class); deserializer.registerAnimal("favorite_toy", Cat.class); deserializer.registerAnimal("wing_span", Bird.class); SimpleModule module = new SimpleModule("PolymorphicAnimalDeserializerModule", new Version(1, 0, 0, null)); module.addDeserializer(Animal.class, deserializer); ObjectMapper mapper = new ObjectMapper(); mapper.setPropertyNamingStrategy( new CamelCaseNamingStrategy()); mapper.registerModule(module); Zoo zoo = mapper.readValue(new File("input_6.json"), Zoo.class); System.out.println(mapper.writeValueAsString(zoo)); } } class AnimalDeserializer extends StdDeserializer<Animal> { private Map<String, Class<? extends Animal>> registry = new HashMap<String, Class<? extends Animal>>(); AnimalDeserializer() { super(Animal.class); } void registerAnimal(String uniqueAttribute, Class<? extends Animal> animalClass) { registry.put(uniqueAttribute, animalClass); } @Override public Animal deserialize( JsonParser jp, DeserializationContext ctxt) throws IOException, JsonProcessingException { ObjectMapper mapper = (ObjectMapper) jp.getCodec(); ObjectNode root = (ObjectNode) mapper.readTree(jp); Class<? extends Animal> animalClass = null; Iterator<Entry<String, JsonNode>> elementsIterator = root.getFields(); while (elementsIterator.hasNext()) { Entry<String, JsonNode> element=elementsIterator.next(); String name = element.getKey(); if (registry.containsKey(name)) { animalClass = registry.get(name); break; } } if (animalClass == null) return null; return mapper.readValue(root, animalClass); } } class Zoo { public Collection<Animal> animals; } abstract class Animal { public String name; } class Dog extends Animal { public String breed; public String leashColor; } class Cat extends Animal { public String favoriteToy; } class Bird extends Animal { public String wingSpan; public String preferredFood; }