杰克逊 - 反序列化使用泛型类

问题描述 投票:97回答:8

我有一个JSON字符串,这是我应该反序列化下面的类

class Data <T> {
    int found;
    Class<T> hits
}

我该怎么做?这是通常的方式

mapper.readValue(jsonString, Data.class);

但我怎么提什么T代表?

java json generics jackson
8个回答
175
投票

您需要创建为您使用的每个泛型类型一个TypeReference对象,并利用它来进行反序列化。例如,

mapper.readValue(jsonString, new TypeReference<Data<String>>() {});

0
投票
public class Data<T> extends JsonDeserializer implements ContextualDeserializer {
    private Class<T> cls;
    public JsonDeserializer createContextual(DeserializationContext ctx, BeanProperty prop) throws JsonMappingException {
        cls = (Class<T>) ctx.getContextualType().getRawClass();
        return this;
    }
    ...
 }

64
投票

你可以这样做:您必须指定完全解决的类型,比如Data<MyType>T仅仅是一个变量,并且是没有意义的。

但是,如果你的意思是T将已知的,只是不是静态,需要动态地创建TypeReference的等价物。其他问题可以参考已经提到这一点,但它应该是这个样子:

public Data<T> read(InputStream json, Class<T> contentClass) {
   JavaType type = mapper.getTypeFactory().constructParametricType(Data.class, contentClass);
   return mapper.readValue(json, type);
}

25
投票

你首先要做的是序列化,那么你可以做反序列化。 所以当你序列化,你应该使用@JsonTypeInfo让杰克逊写类信息到您的JSON数据。你可以做的是这样的:

Class Data <T> {
    int found;
    @JsonTypeInfo(use=JsonTypeInfo.Id.CLASS, include=JsonTypeInfo.As.PROPERTY, property="@class")
    Class<T> hits
}

然后,当你反序列化,你会发现杰克逊已经反序列化的数据到其中的变量实际上打的是在运行时类。


11
投票

对于类数据<>

ObjectMapper mapper = new ObjectMapper();
JavaType type = mapper.getTypeFactory().constructParametrizedType(Data.class, Data.class, Parameter.class);
Data<Parameter> dataParam = mapper.readValue(jsonString,type)

9
投票

只是写的Util类的静态方法。我从一个文件中读取一个JSON。你可以给字符串也readValue

public static <T> T convertJsonToPOJO(String filePath, Class<?> target) throws JsonParseException, JsonMappingException, IOException, ClassNotFoundException {
        ObjectMapper objectMapper = new ObjectMapper();
        return objectMapper.readValue(new File(filePath), objectMapper .getTypeFactory().constructCollectionType(List.class, Class.forName(target.getName())));
}

用法:

List<TaskBean> list =  Util.<List<TaskBean>>convertJsonToPOJO("E:/J2eeWorkspaces/az_workspace_svn/az-client-service/dir1/dir2/filename.json", TaskBean.class);

6
投票

您可以在知道你的泛型类型的类型另一个类包裹。

例如,

class Wrapper {
 private Data<Something> data;
}
mapper.readValue(jsonString, Wrapper.class);

这里的东西是一个具体类型。你需要每物化类型的包装。否则,杰克逊不知道哪些对象创建。


4
投票

需要反序列化JSON字符串必须包含有关参数T类型的信息。 你将不得不把杰克逊注解,可以作为参数TData中传递,因此有关参数类型T类型信息可以从/写入杰克逊JSON字符串来读取每个类。

让我们假设T可以扩展抽象类Result任何类。

class Data <T extends Result> {
    int found;
    Class<T> hits
}

@JsonTypeInfo(use = JsonTypeInfo.Id.NAME, include = JsonTypeInfo.As.WRAPPER_OBJECT)
@JsonSubTypes({
        @JsonSubTypes.Type(value = ImageResult.class, name = "ImageResult"),
        @JsonSubTypes.Type(value = NewsResult.class, name = "NewsResult")})
public abstract class Result {

}

public class ImageResult extends Result {

}

public class NewsResult extends Result {

}

一旦每个作为参数T被注释可以传递的类(或它们的公用超类型)的,杰克逊将包括有关JSON参数T信息。这样JSON然后可以在不知道参数T在编译时反序列化。 这Jackson documentation link约多态性反序列化会谈,但就是指对这个问题也非常有用。


1
投票

从杰克逊2.5,以解决一种优雅的方式是使用TypeFactory.constructParametricType(Class parametrized, Class... parameterClasses)方法,其允许通过指定参数化类和其参数化类型straigthly限定杰克逊JavaType

假设你要反序列化Data<String>,你可以这样做:

// the json variable may be a String, an InputStream and so for...
JavaType type = mapper.getTypeFactory().constructParametricType(Data.class, String.class);
Data<String> data = mapper.readValue(json, type);

需要注意的是,如果一个类中声明多个参数化类型,它不会是真正的困难:

class Data <T, U> {
    int found;
    Class<T> hits;
    List<U> list;
}

我们可以这样做:

JavaType type = mapper.getTypeFactory().constructParametricType(Data.class, String.class, Integer);
Data<String, Integer> data = mapper.readValue(json, type);

0
投票

如果你使用Scala和知道在编译时的泛型类型,但不想手动在你所有的API升艾尔斯到处传TypeReference,您可以使用下面的代码(杰克逊2.9.5):

def read[T](entityStream: InputStream)(implicit typeTag: WeakTypeTag[T]): T = {

    //nathang: all of this *crazy* scala reflection allows us to handle List[Seq[Map[Int,Value]]]] without passing
    // new TypeReference[List[Seq[Map[Int,Value]]]]](){} to the function

    def recursiveFindGenericClasses(t: Type): JavaType = {
      val current = typeTag.mirror.runtimeClass(t)

      if (t.typeArgs.isEmpty) {
        val noSubtypes = Seq.empty[Class[_]]
        factory.constructParametricType(current, noSubtypes:_*)
      }

      else {
        val genericSubtypes: Seq[JavaType] = t.typeArgs.map(recursiveFindGenericClasses)
        factory.constructParametricType(current, genericSubtypes:_*)
      }

    }

    val javaType = recursiveFindGenericClasses(typeTag.tpe)

    json.readValue[T](entityStream, javaType)
  }

可以使用的这样的:

read[List[Map[Int, SomethingToSerialize]]](inputStream)
© www.soinside.com 2019 - 2024. All rights reserved.