如何用Jackson反序列化泛型类?

时间:2014-03-03 23:03:31

标签: java json generics jackson json-deserialization

我有一些类似的课程:

@JsonIgnoreProperties(ignoreUnknown = true)
@JsonSubTypes(
              {
                  @JsonSubTypes.Type(value = LionCage.class, name = "LION"),
                  @JsonSubTypes.Type(value = TigerCage.class, name = "TIGER"),
              }
             )
@JsonTypeInfo(use = JsonTypeInfo.Id.NAME, include = JsonTypeInfo.As.PROPERTY, property = "type")
public abstract class Cage<ANIMAL> {

  private AnimalType type;
  private ANIMAL animal;

}

public enum AnimalType {

    LION, TIGER

}

public LionCage extends Cage<Lion>{

    public LionCage(){
        super(AnimalType.LION);
    }
}

public Lion {

 private int maneLength;

}

public class Zoo {

   private List<Cage<?>> cages = new LinkedList<Cage<?>>();

}

现在,如果我尝试执行以下操作:

String json = "{cages: [{\"type\": \"LION\", \"animal\": {\"maneLength\" : 10}}]}";

ObjectMapper om = new ObjectMapper();

Cage cage = om.readValue(json, Zoo.class);

网箱中的第一个值应该是LionCage类型,而且是。

问题是cages.get(0).getAnimal()会返回LinkedHashMap如何让它返回正确的Animal子类?

我正在使用Jackson 1。

谢谢!

2 个答案:

答案 0 :(得分:4)

对于您的List问题,这是您想要使用原始类型的一次。将Cage类型参数声明为raw,而不是使用通配符进行参数化。

public class Zoo {

    private List<Cage> cages = new LinkedList<Cage>();
    // ... getters and setters similarly
}

使用通配符,您告诉Jackson它没关系,所以它使用默认值LinkedHashMap。如果删除通配符并使用原始类型,则需要在其他位置查找提示。在这种情况下,它将查看它使用的实际Cage实现。


您的JSON应该如下所示

String json = "{\"type\": \"LION\", \"animal\": {\"maneLength\" : 10}}";

杰克逊将能够确定animalLion

通过type,它会知道它是LionCage并通过

的类定义
public LionCage extends Cage<Lion>{

它会知道对类型变量Animal的任何引用实际上应该是Lion

我在杰克逊2上。

但它也适用于Jackson 1。代码

import org.codehaus.jackson.annotate.JsonIgnoreProperties;
import org.codehaus.jackson.annotate.JsonSubTypes;
import org.codehaus.jackson.annotate.JsonTypeInfo;
import org.codehaus.jackson.map.ObjectMapper;

public class Example {

    public static void main(String[] args) throws Exception {
        String json = "{\"type\": \"LION\", \"animal\": {\"maneLength\" : 10}}";

        ObjectMapper om = new ObjectMapper();

        Cage cage = om.readValue(json, Cage.class);
        System.out.println(cage.getClass());
        System.out.println(cage.getAnimal());

        System.out.println(((Lion) cage.getAnimal()).getManeLength());
    }
}

class Lion {

    private int maneLength;

    public int getManeLength() {
        return maneLength;
    }

    public void setManeLength(int maneLength) {
        this.maneLength = maneLength;
    }
}

class LionCage extends Cage<Lion> {

    public LionCage() {
        super(AnimalType.LION);
    }
}

class Tiger {
}

class TigerCage extends Cage<Tiger> {

    public TigerCage() {
        super(AnimalType.TIGER);
    }
}

@JsonIgnoreProperties(ignoreUnknown = true)
@JsonSubTypes({ @JsonSubTypes.Type(value = LionCage.class, name = "LION"),
        @JsonSubTypes.Type(value = TigerCage.class, name = "TIGER"), })
@JsonTypeInfo(use = JsonTypeInfo.Id.NAME, include = JsonTypeInfo.As.PROPERTY, property = "type")
abstract class Cage<Animal> {

    public Cage(AnimalType type) {
        this.setType(type);
    }

    public AnimalType getType() {
        return type;
    }

    public void setType(AnimalType type) {
        this.type = type;
    }

    public Animal getAnimal() {
        return animal;
    }

    public void setAnimal(Animal animal) {
        this.animal = animal;
    }

    private AnimalType type;
    private Animal animal;
}

enum AnimalType {
    LION, TIGER;
}

打印

class com.spring.LionCage
com.spring.Lion@15d16efc
10

答案 1 :(得分:4)

我认为您应该通过删除不必要的类型并使用JsonTypeInfo.As.EXTERNAL_PROPERTY代替JsonTypeInfo.As.PROPERTY来简化您的解决方案。使用JsonTypeInfo.As.EXTERNAL_PROPERTY,您不必创建Cage子类。而且您根本不必创建AnimalType。你POJO课程可能如下所示:

@JsonIgnoreProperties(ignoreUnknown = true)
class Cage<ANIMAL> {

    @JsonSubTypes({
        @JsonSubTypes.Type(value = Lion.class, name = "LION"),
        @JsonSubTypes.Type(value = Tiger.class, name = "TIGER")
    })
    @JsonTypeInfo(use = JsonTypeInfo.Id.NAME, include = JsonTypeInfo.As.EXTERNAL_PROPERTY, property = "type")
    private ANIMAL animal;

    public ANIMAL getAnimal() {
        return animal;
    }

    public void setAnimal(ANIMAL animal) {
        this.animal = animal;
    }

    @Override
    public String toString() {
        return "Cage [animal=" + animal + "]";
    }
}

class Lion {

    private int maneLength;

    public int getManeLength() {
        return maneLength;
    }

    public void setManeLength(int maneLength) {
        this.maneLength = maneLength;
    }

    @Override
    public String toString() {
        return "Lion [maneLength=" + maneLength + "]";
    }
}

class Tiger {

    private int length;

    public int getLength() {
        return length;
    }

    public void setLength(int length) {
        this.length = length;
    }

    @Override
    public String toString() {
        return "Tiger [maneLength=" + length + "]";
    }
}

简单用法:

String jsonLion = "{\"type\": \"LION\", \"animal\": {\"maneLength\" : 10}}";
String jsonTiger = "{\"type\": \"TIGER\", \"animal\": {\"length\" : 11}}";

ObjectMapper om = new ObjectMapper();

System.out.println(om.readValue(jsonLion, Cage.class));
System.out.println(om.readValue(jsonTiger, Cage.class));

以上代码打印:

Cage [animal=Lion [maneLength=10]]
Cage [animal=Tiger [maneLength=11]]

我没有使用Jackson 1.X测试我的解决方案,但我希望它对您有用。