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

时间:2015-05-21 00:12:35

标签: java json compiler-errors polymorphism jackson

我正在尝试使用Programmer Bruce的一个教程,该教程应该允许对多态JSON进行反序列化。

完整列表可在此处找到 Programmer Bruce tutorials(很棒的东西顺便说一下)

我已经完成了前五个问题但没有遇到任何问题,但我在最后一个问题上遇到了障碍(例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);
  }  
} 

特别是行

  

return mapper.readValue(root,animalClass);

之前是否有人遇到此问题,如果有,是否有解决方案?

我很感激任何人都能给予的帮助 提前致谢 Jon D.

6 个答案:

答案 0 :(得分:99)

正如所承诺的,我正在举例说明如何使用注释来序列化/反序列化多态对象,我在你正在阅读的教程的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;
    }

}

然后是您的子类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;

}

正如您所看到的,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();
        }

    }
}

运行Test类后输出:

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

Deserialized dogJson Class: Dog

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

Deserialized catJson Class: Cat

希望这有帮助,

何塞路易斯

答案 1 :(得分:5)

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

当您无法更改传入的 json 或不得更改时,它很有用。在无法确定使用哪个子类型的复杂情况下,我仍然建议使用 use = JsonTypeInfo.Id.NAME 作为新方法 may throw an exception

现在你可以简单地写

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

}

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

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

答案 2 :(得分:2)

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

为此,请确保正确配置了映射器。 例如:

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

jacksonObjectMapper.enableDefaultTyping(
    ObjectMapper.DefaultTyping.OBJECT_AND_NON_CONCRETE); 

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

jacksonObjectMapper.enableDefaultTyping(
    ObjectMapper.DefaultTyping.NON_CONCRETE_AND_ARRAYS); 

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

答案 3 :(得分:1)

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

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

此行的意思是:在序列化上添加元属性,或者在反序列化(include = JsonTypeInfo.As.PROPERTY)上读取元属性,称为“ @class”(property = "@class"),其中包含标准Java类名。 (use = JsonTypeInfo.Id.CLASS

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

更多信息here

答案 4 :(得分:0)

如果使用的是fastxml,那么

可能需要进行这些更改

var mode;
initialize: function(element, options){
    if(element.isTextbox1){
        mode = 'textbox1';
    }
    else{
        mode = 'textbox2';
    }
    return element[mode](options);
}
主要方法中的

-

使用

import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.core.Version;
import com.fasterxml.jackson.databind.DeserializationContext;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.deser.std.StdDeserializer;
import com.fasterxml.jackson.databind.module.SimpleModule;
import com.fasterxml.jackson.databind.node.ObjectNode;

而不是

SimpleModule module =
  new SimpleModule("PolymorphicAnimalDeserializerModule");

并在Animal deserialize()函数中,进行以下更改

new SimpleModule("PolymorphicAnimalDeserializerModule",
      new Version(1, 0, 0, null));

这适用于fasterxml.jackson。如果它仍然抱怨类字段。使用与json中相同的格式作为字段名称(使用&#34; _&#34; -underscore)。如此 //Iterator<Entry<String, JsonNode>> elementsIterator = root.getFields(); Iterator<Entry<String, JsonNode>> elementsIterator = root.fields(); //return mapper.readValue(root, animalClass); return mapper.convertValue(root, animalClass);  可能不受支持。

//mapper.setPropertyNamingStrategy(new CamelCaseNamingStrategy());

答案 5 :(得分:0)

处理多态要么是模型绑定的,要么需要大量带有各种自定义反序列化器的代码。我是 JSON Dynamic Deserialization Library 的合著者,它允许独立于模型的 json 反序列化库。可以在下面找到 OP 问题的解决方案。请注意,这些规则的声明方式非常简短。

public class SOAnswer {
    @ToString @Getter @Setter
    @AllArgsConstructor @NoArgsConstructor
    public static abstract class Animal {
        private String name;    
    }

    @ToString(callSuper = true) @Getter @Setter
    @AllArgsConstructor @NoArgsConstructor
    public static class Dog extends Animal {
        private String breed;
    }

    @ToString(callSuper = true) @Getter @Setter
    @AllArgsConstructor @NoArgsConstructor
    public static class Cat extends Animal {
        private String favoriteToy;
    }
    
    
    public static void main(String[] args) {
        String json = "[{"
                + "    \"name\": \"pluto\","
                + "    \"breed\": \"dalmatian\""
                + "},{"
                + "    \"name\": \"whiskers\","
                + "    \"favoriteToy\": \"mouse\""
                + "}]";
        
        // create a deserializer instance
        DynamicObjectDeserializer deserializer = new DynamicObjectDeserializer();
        
        // runtime-configure deserialization rules; 
        // condition is bound to the existence of a field, but it could be any Predicate
        deserializer.addRule(DeserializationRuleFactory.newRule(1, 
                (e) -> e.getJsonNode().has("breed"),
                DeserializationActionFactory.objectToType(Dog.class)));
        
        deserializer.addRule(DeserializationRuleFactory.newRule(1, 
                (e) -> e.getJsonNode().has("favoriteToy"),
                DeserializationActionFactory.objectToType(Cat.class)));
        
        List<Animal> deserializedAnimals = deserializer.deserializeArray(json, Animal.class);
        
        for (Animal animal : deserializedAnimals) {
            System.out.println("Deserialized Animal Class: " + animal.getClass().getSimpleName()+";\t value: "+animal.toString());
        }
    }
}

pretius-jddl 的 Maven 依赖(在 maven.org/jddl 查看最新版本:

<dependency>
  <groupId>com.pretius</groupId>
  <artifactId>jddl</artifactId>
  <version>1.0.0</version>
</dependency>