239

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?

Todd
  • 30,472
  • 11
  • 81
  • 89
gnjago
  • 3,391
  • 5
  • 19
  • 17
  • 1
    possible duplicate of [Jackson - Deserialize Generic class variable](http://stackoverflow.com/questions/11659844/jackson-deserialize-generic-class-variable) – Programmer Bruce Jul 26 '12 at 16:10
  • see http://stackoverflow.com/questions/17400850/is-jackson-really-unable-to-deserialize-json-into-a-generic-type – lisak Jul 01 '13 at 12:49

10 Answers10

399

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>>() {});
Naman
  • 27,789
  • 26
  • 218
  • 353
Eser Aygün
  • 7,794
  • 1
  • 20
  • 30
  • I have to use it as TypeReference>(){} ... But I am getting the following error - cannot access private java.lang.class.Class() from java.lang.class. Failed to set access. Cannot make a java.lang.Class constructor accessible – gnjago Jul 26 '12 at 19:46
  • No, not `Data`, that is NOT a type. You must specify actual class; otherwise it is same as `Data`. – StaxMan Jul 27 '12 at 04:38
  • 39
    What if I dont know what class it is until runtime? I will get the class as a parameter during runtime. Like this public void deSerialize(Class clazz { ObjectMapper mapper = new ObjectMapper(); mapper.readValue(jsonString, new TypeReference>() {}); } – gnjago Jul 27 '12 at 05:43
  • 3
    I have asked the full question correctly here http://stackoverflow.com/questions/11659844/jackson-deserialize-generic-class-variable – gnjago Jul 27 '12 at 05:49
  • 3
    what's the full package name of `TypeReference`? is it `com.fasterxml.jackson.core.type`? – Lei Yang May 25 '20 at 02:04
  • @LeiYang Yes, it seems to be – Heath Mitchell Dec 26 '20 at 17:28
  • 5
    There is no need to define TypeReference type if your left-handed assignment reference has a type, just: `mapper.readValue(jsonString, new TypeReference<>() {});` – Zon Mar 17 '22 at 12:50
124

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);
   return mapper.readValue(json, type);
}
Dave
  • 764
  • 7
  • 17
StaxMan
  • 113,358
  • 34
  • 211
  • 239
  • 3
    What if I dont know what class it is until runtime? I will get the class as a parameter during runtime. Like this public void deSerialize(Class clazz { ObjectMapper mapper = new ObjectMapper(); mapper.readValue(jsonString, new TypeReference>() {}); } – gnjago Jul 27 '12 at 05:45
  • 1
    I have asked the full question here http://stackoverflow.com/questions/11659844/jackson-deserialize-generic-class-variable – gnjago Jul 27 '12 at 05:49
  • 3
    Then just pass the class as is, no need for `TypeReference`: `return mapper.readValue(json, clazz);` What exactly is the problem here? – StaxMan Jul 27 '12 at 17:37
  • 2
    The problem is that "Data" is a generic class. I need to specify what type T is at runtime. The parameter clazz is what T us at runtime. So, how to call readValue? calling it with new TypeReference>Json> does not work The full question is here http://stackoverflow.com/questions/11659844/jackson-deserialize-generic-class-variable – gnjago Jul 27 '12 at 18:28
  • 2
    Ok. Then you need to use `TypeFactory`.. I will edit my answer. – StaxMan Jul 27 '12 at 19:19
  • @StaxMan May I ask what you'd have as second argument in the readValue() method so as to return a List ? – Stephane Sep 01 '14 at 13:36
  • 1
    @StephaneEybert you'd need to use `TypeFactory` to construct the type; you can get an instance of factory from `ObjectMapper` – StaxMan Sep 08 '14 at 18:19
  • 4
    In Jackson 2.5 and above you should use `TypeFactory.constructParametrizedType` as `TypeFactory. constructParametricType` is deprecated. – David V Jun 12 '17 at 19:29
  • 1
    @DavidV You mean the contrary? Anyway, this should be the accepted answer. – Marco Sulla Sep 11 '19 at 16:18
  • 1
    `constructParametricType` was briefly deprecated in 2.6 but only in that minor version: it replaces `constructParametrizedType()` which is deprecated. – StaxMan Sep 11 '19 at 16:50
  • This solution worked for me thanks @StaxMan – Shikhar Chaudhary Oct 26 '21 at 04:46
40

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.

Beri
  • 11,470
  • 4
  • 35
  • 57
CharlieQ
  • 839
  • 7
  • 10
  • not working, getting below error com.fasterxml.jackson.databind.exc.InvalidTypeIdException: Missing type id when trying to resolve subtype of [simple type, class java.lang.Object]: missing type id property '@class' (for POJO property 'data') – gaurav9620 Apr 08 '20 at 11:49
31

From Jackson 2.5, an elegant way to solve that is using the TypeFactory.constructParametricType(Class parametrized, Class... parameterClasses) method that allows to define straigthly a Jackson JavaType by specifying the parameterized class and its parameterized types.

Supposing you want to deserialize to Data<String>, you can do :

// 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);

Note that if the class declared multiple parameterized types, it would not be really harder :

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

We could do :

JavaType type = mapper.getTypeFactory().constructParametricType(Data.class, String.class, Integer);
Data<String, Integer> data = mapper.readValue(json, type);
davidxxx
  • 125,838
  • 23
  • 214
  • 215
  • Awesome, thanks it worked for me. With the typereference I got classcast exception from map to the specific object but this really do the job. – Tacsiazuma Jun 09 '20 at 08:17
16

For class Data<>

ObjectMapper mapper = new ObjectMapper();
JavaType type = mapper.getTypeFactory().constructParametrizedType(Data.class, Data.class, Parameter.class);
Data<Parameter> dataParam = mapper.readValue(jsonString,type)
Devesh
  • 191
  • 1
  • 5
15

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);
AZ_
  • 21,688
  • 25
  • 143
  • 191
7

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.

sksamuel
  • 16,154
  • 8
  • 60
  • 108
7

JSON string that needs to be deserialized will have to contain the type information about parameter T.
You will have to put Jackson annotations on every class that can be passed as parameter T to class Data so that the type information about parameter type T can be read from / written to JSON string by Jackson.

Let us assume that T can be any class that extends abstract class 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 {

}

Once each of the class (or their common supertype) that can be passed as parameter T is annotated, Jackson will include information about parameter T in the JSON. Such JSON can then be deserialized without knowing the parameter T at compile time.
This Jackson documentation link talks about Polymorphic Deserialization but is useful to refer to for this question as well.

Sanjeev Sachdev
  • 1,241
  • 1
  • 15
  • 23
1
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;
    }
    ...
 }
mikka22
  • 11
  • 1
-1

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)
JJD
  • 50,076
  • 60
  • 203
  • 339
nathan g
  • 858
  • 9
  • 17