Jackson - Deserialize using generic class

2019-01-03 06:09发布

I have a json string, which I should deSerialize to the following class

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

How do I do it? This is the usual way

mapper.readValue(jsonString, Data.class);

But how do I mention what T stands for?

8条回答
何必那么认真
2楼-- · 2019-01-03 06:14

if you're using scala and know the generic type at compile time, but don't want to manually pass TypeReference everywhere in all your api l ayers, you can use the following code (with jackson 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)

}

which can be used like this:

read[List[Map[Int, SomethingToSerialize]]](inputStream)

查看更多
Fickle 薄情
3楼-- · 2019-01-03 06:18

You need to create a TypeReference object for each generic type you use and use that for deserialization. For example,

mapper.readValue(jsonString, new TypeReference<Data<String>>() {});
查看更多
等我变得足够好
4楼-- · 2019-01-03 06:19

First thing you do is serialize, then you can do deserialize.
so when you do serialize, you should use @JsonTypeInfo to let jackson write class information into your json data. What you can do is like this:

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

Then when you deserialize, you will find jackson has deserialize your data into a class which your variable hits actually is at runtime.

查看更多
beautiful°
5楼-- · 2019-01-03 06:20

You can't do that: you must specify fully resolved type, like Data<MyType>. T is just a variable, and as is meaningless.

But if you mean that T will be known, just not statically, you need to create equivalent of TypeReference dynamically. Other questions referenced may already mention this, but it should look something like:

public Data<T> read(InputStream json, Class<T> contentClass) {
   JavaType type = mapper.getTypeFactory().constructParametricType(Data.class, contentClass.class);
   return mapper.readValue(json, type);
}
查看更多
干净又极端
6楼-- · 2019-01-03 06:23

Just write a static method in Util class. I am reading a Json from a file. you can give String also to 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())));
}

Usage:

List<TaskBean> list =  Util.<List<TaskBean>>convertJsonToPOJO("E:/J2eeWorkspaces/az_workspace_svn/az-client-service/dir1/dir2/filename.json", TaskBean.class);
查看更多
何必那么认真
7楼-- · 2019-01-03 06:26

You can wrap it in another class which knows the type of your generic type.

Eg,

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

Here Something is a concrete type. You need a wrapper per reified type. Otherwise Jackson does not know what objects to create.

查看更多
登录 后发表回答