【问题标题】:Jackson deserialization of type with different objectsJackson 使用不同对象对类型进行反序列化
【发布时间】:2013-12-07 05:31:29
【问题描述】:

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

【问题讨论】:

    标签: java jackson deserialization


    【解决方案1】:

    我问了一个类似的问题 - JSON POJO consumer of polymorphic objects

    您必须编写自己的deserialiser,以便在反序列化过程中查看并根据数据决定要做什么。

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

    【讨论】:

    • 我无法让该解决方案为我工作。我在 InfoDeserializer 的末尾收到“由于输入结束而没有要映射的内容”错误。
    • 我确实让它与自定义解串器一起工作,但 StaxMan 的解决方案对我来说更干净。
    【解决方案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;
        }
    }
    

    现在您必须告诉 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
    }
    

    【讨论】:

    • 谢谢,最初我是这样做的,但 StaxMan 的解决方案在我看来更简洁,并且减少了我需要的实际课程数量
    【解决方案3】:

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

    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 构造,从标量构造。但我认为上面应该可行。

    【讨论】:

    • 我发誓我以前试过这个方法!我用最终解决方案更新了我的问题。非常感谢
    【解决方案4】:

    我发现了一篇关于这个主题的好文章: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;
    }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2018-12-05
      • 2020-08-07
      • 2011-07-10
      • 1970-01-01
      • 2014-02-12
      • 2022-01-22
      • 1970-01-01
      • 2020-11-28
      相关资源
      最近更新 更多