用杰克逊将JSON反序列化为多态类型 - 完整示例给出了编译错误

Jon*_*oll 62 java polymorphism json compiler-errors jackson

我试图通过程序员布鲁斯的一个教程来完成,该教程应该允许多态JSON的反序列化.

完整的列表可以在这里找到 程序员布鲁斯教程(伟大的东西顺便说一句)

我已经完成了前五个没有问题,但我在最后一个(示例6)遇到了障碍,当然这是我真正需要工作的那个.

我在编译时遇到以下错误

ObjectMapper类型中的方法readValue(JsonParser,Class)不适用于参数(ObjectNode,Class)

它是由大量代码引起的

  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);
  }  
} 
Run Code Online (Sandbox Code Playgroud)

具体由行

return mapper.readValue(root,animalClass);

有没有人遇到过这个问题,如果有的话,是否有解决方案?

我很感激任何人都可以给予任何帮助.

jba*_*eta 110

正如所承诺的,我正在举例说明如何使用注释来序列化/反序列化多态对象,我在Animal您正在阅读的教程的类中基于此示例.

首先,您的Animal类包含子类的Json Annotations.

import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.fasterxml.jackson.annotation.JsonSubTypes;
import com.fasterxml.jackson.annotation.JsonTypeInfo;

@JsonIgnoreProperties(ignoreUnknown = true)
@JsonTypeInfo(use = JsonTypeInfo.Id.NAME, include = JsonTypeInfo.As.PROPERTY)
@JsonSubTypes({
    @JsonSubTypes.Type(value = Dog.class, name = "Dog"),

    @JsonSubTypes.Type(value = Cat.class, name = "Cat") }
)
public abstract class Animal {

    private String name;

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

}
Run Code Online (Sandbox Code Playgroud)

然后你的子类,DogCat.

public class Dog extends Animal {

    private String breed;

    public Dog() {

    }

    public Dog(String name, String breed) {
        setName(name);
        setBreed(breed);
    }

    public String getBreed() {
        return breed;
    }

    public void setBreed(String breed) {
        this.breed = breed;
    }
}

public class Cat extends Animal {

    public String getFavoriteToy() {
        return favoriteToy;
    }

    public Cat() {}

    public Cat(String name, String favoriteToy) {
        setName(name);
        setFavoriteToy(favoriteToy);
    }

    public void setFavoriteToy(String favoriteToy) {
        this.favoriteToy = favoriteToy;
    }

    private String favoriteToy;

}
Run Code Online (Sandbox Code Playgroud)

正如你所看到的,没有什么特别的CatDog,即了解他们的唯一一个是abstractAnimal,所以反序列化时,就定位于AnimalObjectMapper返回的实际情况,你可以在下面的测试中看到:

public class Test {

    public static void main(String[] args) {

        ObjectMapper objectMapper = new ObjectMapper();

        Animal myDog = new Dog("ruffus","english shepherd");

        Animal myCat = new Cat("goya", "mice");

        try {
            String dogJson = objectMapper.writeValueAsString(myDog);

            System.out.println(dogJson);

            Animal deserializedDog = objectMapper.readValue(dogJson, Animal.class);

            System.out.println("Deserialized dogJson Class: " + deserializedDog.getClass().getSimpleName());

            String catJson = objectMapper.writeValueAsString(myCat);

            Animal deseriliazedCat = objectMapper.readValue(catJson, Animal.class);

            System.out.println("Deserialized catJson Class: " + deseriliazedCat.getClass().getSimpleName());



        } catch (Exception e) {
            e.printStackTrace();
        }

    }
}
Run Code Online (Sandbox Code Playgroud)

运行Test类后输出:

{"@type":"Dog","name":"ruffus","breed":"english shepherd"}

Deserialized dogJson Class: Dog

{"@type":"Cat","name":"goya","favoriteToy":"mice"}

Deserialized catJson Class: Cat

希望这可以帮助,

何塞路易斯

  • 如果我在没有@type信息的情况下从Retrofit获取对象怎么办? (6认同)
  • 是否有可能做到这一点,而无需使用@JsonSubTypes注解?这样子类可以在其他包中加入原班已经撰写了之后? (5认同)
  • @HDave我已经用`@ JsonTypeIdResolver`建立了一个要点,它可能会做你所需要的:https://gist.github.com/root-talis/36355f227ff5bb7a057ff7ad842d37a3 (4认同)
  • @KamalJoshi 是的,如果 Animal 是一个接口,你也可以这样做。刚刚通过将 Animal 更改为接口进行了测试,并且相同的主要方法有效。关于你的第二个问题,名称与注释中的“Dog”无关是正确的,“name”只是序列化类的一个字段,可能为了避免混淆,我可以在“Animal”中使用“petName” `类而不是`名称`。jackson 注释的“name”字段是设置为序列化 Json 作为标识符的值。HTH。 (3认同)
  • @HDave 我相信您可以实现自己的`@JsonTypeIdResolver`,它将使用反射来确定可能的子类列表并将您的@type 绑定到它们的名称。可以在此处找到示例实现:https://www.thomaskeller.biz/blog/2013/09/10/custom-polymorphic-type-handling-with-jackson/ (2认同)
  • @user1300959 不,他们不需要实现“Serialized”来使这个示例工作。 (2认同)
  • 这不可能是一种方法。世界上哪里有一个基抽象类知道每个可能的子类?这在设计上应该是很糟糕的。 (2认同)

Mar*_*rco 16

您只需要在类声明前一行就可以Animal进行正确的多态序列化/反序列化:

@JsonTypeInfo(use = JsonTypeInfo.Id.CLASS, include = JsonTypeInfo.As.PROPERTY, property = "@class")
public abstract class Animal {
   ...
}
Run Code Online (Sandbox Code Playgroud)

此行表示:在序列化时添加元属性或在反序列化时读取元属性 ( include = JsonTypeInfo.As.PROPERTY),称为“@class”( property = "@class"),其中包含完全限定的 Java 类名 ( use = JsonTypeInfo.Id.CLASS)。

因此,如果您直接创建 JSON(不进行序列化),请记住添加具有所需类名的元属性“@class”以进行正确的反序列化。

更多信息在这里


Xob*_*tun 8

虽然@jbarrueta 的答案是完美的,但在 2.12 版本的 Jackson 中引入了一个新的期待已久的@JsonTypeInfo注释类型DEDUCTION.

当您无法更改传入的 json 或不得更改时,它很有用。我仍然建议使用use = JsonTypeInfo.Id.NAME,因为在无法确定使用哪个子类型的复杂情况下,新方法可能会抛出异常

现在你可以简单地写

import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.fasterxml.jackson.annotation.JsonSubTypes;
import com.fasterxml.jackson.annotation.JsonTypeInfo;

@JsonIgnoreProperties(ignoreUnknown = true)
@JsonTypeInfo(use = JsonTypeInfo.Id.DEDUCTION)
@JsonSubTypes({
    @JsonSubTypes.Type(Dog.class),
    @JsonSubTypes.Type(Cat.class) }
)
public abstract class Animal {

    private String name;

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

}
Run Code Online (Sandbox Code Playgroud)

它会产生{"name":"ruffus", "breed":"english shepherd"}{"name":"goya", "favoriteToy":"mice"}

再一次,NAME如果某些字段可能不存在,则使用更安全,例如breedfavoriteToy

  • 如果使用 Jackson 2.12+,这是最合适的答案。 (3认同)

Ami*_*itW 6

通过 Jackson 库启用多态序列化/反序列化的一个简单方法是全局配置 Jackson 对象映射器 (jackson.databind.ObjectMapper) 来为某些类型的类(例如抽象类)添加信息,例如具体类类型。

为此,只需确保您的映射器配置正确即可。例如:

选项 1:支持抽象类(和对象类型类)的多态序列化/反序列化

jacksonObjectMapper.enableDefaultTyping(
    ObjectMapper.DefaultTyping.OBJECT_AND_NON_CONCRETE); 
Run Code Online (Sandbox Code Playgroud)

选项 2:支持抽象类(和对象类型类)以及这些类型的数组的多态序列化/反序列化。

jacksonObjectMapper.enableDefaultTyping(
    ObjectMapper.DefaultTyping.NON_CONCRETE_AND_ARRAYS); 
Run Code Online (Sandbox Code Playgroud)

参考: https: //github.com/FasterXML/jackson-docs/wiki/JacksonPolymorphicDeserialization