Java Jackson 反序列化不同对象的类型

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/20143114/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-12 23:28:48  来源:igfitidea点击:

Hymanson deserialization of type with different objects

javaHymansondeserialization

提问by Omertron

I have a result from a web service that returns either a boolean value or a singleton map, e.g.

我有一个返回布尔值或单例映射的 Web 服务的结果,例如

Boolean result:

布尔结果:

{
    id: 24428,
    rated: false
}

Map result:

地图结果:

{
    id: 78,
    rated: {
        value: 10
    }
}

Individually I can map both of these easily, but how do I do it generically?

单独我可以轻松地映射这两个,但我一般如何做?

Basically I want to map it to a class like:

基本上我想将它映射到一个类,如:

public class Rating {
    private int id;
    private int rated;
    ...
    public void setRated(?) {
        // if value == false, set rated = -1;
        // else decode "value" as rated
    }
}

All of the polymorphic examples use @JsonTypeInfoto map based on a property in the data, but I don't have that option in this case.

所有多态示例都@JsonTypeInfo用于基于数据中的属性进行映射,但在这种情况下我没有该选项。



EDIT编辑


更新的代码部分:

@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();
    }
}

采纳答案by StaxMan

No no no. You do NOT have to write a custom deserializer. Just use "untyped" mapping first:

不不不。您不必编写自定义解串器。只需先使用“无类型”映射:

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

which gives value of Booleanor java.util.Mapfor "rated" (with first approach); or a JsonNodein second case.

它给出了“额定”Booleanjava.util.Map“额定”的值(第一种方法);或JsonNode第二种情况。

From that, you can either access data as is, or, perhaps more interestingly, convert to actual value:

从那里,您可以按原样访问数据,或者更有趣的是,转换为实际值:

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)

There are other kinds of approaches too -- using creator "ActualRated(boolean)", to let instance constructed either from POJO, orfrom scalar. But I think above should work.

还有其他类型的方法——使用创建者“ActualRated(boolean)”,让实例从 POJO从标量构造。但我认为以上应该有效。

回答by OldCurmudgeon

I asked a similar question - JSON POJO consumer of polymorphic objects

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

You have to write your own deserialiserthat gets a look-in during the deserialise process and decides what to do depending on the data.

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

There may be other easier methods but this method worked well for me.

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

回答by Micha? Ziober

You have to write your own deserializer. It could look like this:

您必须编写自己的解串器。它可能看起来像这样:

@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;
    }
}

Now you have to tell Hymanson to use this deserializer for Ratingclass:

现在你必须告诉Hyman逊在Rating课堂上使用这个反序列化器:

@JsonDeserialize(using = RatingJsonDeserializer.class)
class Rating {
...
}

Simple usage:

简单用法:

ObjectMapper objectMapper = new ObjectMapper();
System.out.println(objectMapper.readValue(json, Rating.class));

Above program prints:

以上程序打印:

Rating [id=78, rated=10]

for JSON:

对于 JSON:

{
    "id": 78,
    "rated": {
        "value": 10
    }
}

and prints:

和打印:

Rating [id=78, rated=-1]

for JSON:

对于 JSON:

{
    "id": 78,
    "rated": false
}

回答by Asaf Bartov

I found a nice article on the subject: http://programmerbruce.blogspot.com/2011/05/deserialize-json-with-Hymanson-into.html

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

I think that the approach of parsing into object, is possibly problematic, because when you send it, you send a string. I am not sure it is an actual issue, but it sounds like some possible unexpected behavior. example 5 and 6 show that you can use inheritance for this.

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

Example:

例子:

Example 6: Simple Deserialization Without Type Element To Container Object With Polymorphic Collection

Some real-world JSON APIs have polymorphic type members, but don't include type elements (unlike the JSON in the previous examples). Deserializing such sources into polymorphic collections is a bit more involved. Following is one relatively simple solution. (This example includes subsequent serialization of the deserialized Java structure back to input JSON, but the serialization is relatively uninteresting.)

// 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.Hymanson.JsonNode;
import org.codehaus.Hymanson.JsonParser;
import org.codehaus.Hymanson.JsonProcessingException;
import org.codehaus.Hymanson.Version;
import org.codehaus.Hymanson.map.DeserializationContext;
import org.codehaus.Hymanson.map.ObjectMapper;
import org.codehaus.Hymanson.map.deser.StdDeserializer;
import org.codehaus.Hymanson.map.module.SimpleModule;
import org.codehaus.Hymanson.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;
}

示例 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.Hymanson.JsonNode;
import org.codehaus.Hymanson.JsonParser;
import org.codehaus.Hymanson.JsonProcessingException;
import org.codehaus.Hymanson.Version;
import org.codehaus.Hymanson.map.DeserializationContext;
import org.codehaus.Hymanson.map.ObjectMapper;
import org.codehaus.Hymanson.map.deser.StdDeserializer;
import org.codehaus.Hymanson.map.module.SimpleModule;
import org.codehaus.Hymanson.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;
}