杰克逊反序列化的类型与不同的对象

时间:2013-11-22 10:54:20

标签: java jackson deserialization

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

4 个答案:

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

为“评级”提供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,构造。但我认为上面应该有效。

答案 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;
}