jackson反序列化的类型与不同的对象

我有一个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(); } } 

不不不。 您不必编写自定义反序列化程序。 首先使用“无类型”映射:

 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); 

它给出了“额定”的Booleanjava.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 标量构造。 但我认为上面应该有效。

你必须编写自己的反序列化器。 它可能看起来像这样:

 @SuppressWarnings("unchecked") class RatingJsonDeserializer extends JsonDeserializer { @Override public Rating deserialize(JsonParser jp, DeserializationContext ctxt) throws IOException, JsonProcessingException { Map map = jp.readValueAs(Map.class); Rating rating = new Rating(); rating.setId(getInt(map, "id")); rating.setRated(getRated(map)); return rating; } private int getInt(Map map, String propertyName) { Object object = map.get(propertyName); if (object instanceof Number) { return ((Number) object).intValue(); } return 0; } private int getRated(Map 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) object), "value"); } return 0; } } 

现在你必须告诉jackson使用这个反序列化器来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 } 

我问了一个类似的问题 – JSON POJO多态对象的消费者

您必须编写自己的deserialiser ,在反序列化过程中查看并根据数据决定要执行的操作。

可能还有其他更简单的方法,但这种方法对我来说效果很好。

我发现了一篇关于这个主题的好文章: http : //programmerbruce.blogspot.com/2011/05/deserialize-json-with-jackson-into.html

我认为解析到对象的方法可能有问题,因为当你发送它时,你发送一个字符串。 我不确定这是一个实际问题,但听起来像是一些可能的意外行为。 示例5和6显示您可以为此使用inheritance。

例:

示例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 { private Map> registry = new HashMap>(); AnimalDeserializer() { super(Animal.class); } void registerAnimal(String uniqueAttribute, Class 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 animalClass = null; Iterator> elementsIterator = root.getFields(); while (elementsIterator.hasNext()) { Entry 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 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; }