38

I had posted the question wrongly. I am posting the question correctly here ...

I am getting a json string as a HTTP response. I know the structure of it. It is as follows:

public class Json<T> {
    public Hits<T> hits;
}
public class Hits<T> {
    public int found;
    public int start;
    public ArrayList<Hit<T>> hit;
}
public class Hit<T> {
    public String id;
    public Class<T> data;
}

The "data" field can belong to any class. I will know it at runtime only. I will get it as a parameter. This is how I am deserializing.

public <T> void deSerialize(Class<T> clazz) {
    ObjectMapper mapper = new ObjectMapper();
    mapper.readValue(jsonString,  new TypeReference<Json<T>>() {});
}

But I am getting an error -

cannot access private java.lang.class.Class() from java.lang.class. Failed to set access. Cannot make a java.lang.Class constructor accessible

moffeltje
  • 4,521
  • 4
  • 33
  • 57
gnjago
  • 3,391
  • 5
  • 19
  • 17
  • Why duplicating the question (see http://stackoverflow.com/questions/11664894/jackson-deserialize-using-generic-class/11681540)? – StaxMan Jul 27 '12 at 19:25
  • see http://stackoverflow.com/questions/17400850/is-jackson-really-unable-to-deserialize-json-into-a-generic-type – lisak Jul 01 '13 at 12:51
  • if you're using scala you can do something like this: https://stackoverflow.com/a/49998502/1009305 – nathan g Apr 24 '18 at 09:52

6 Answers6

39

You will need to build JavaType explicitly, if generic type is only dynamically available:

// do NOT create new ObjectMapper per each request!
final ObjectMapper mapper = new ObjectMapper();

public Json<T> void deSerialize(Class<T> clazz, InputStream json) {
    return mapper.readValue(json,
      mapper.getTypeFactory().constructParametricType(Json.class, clazz));
}
StaxMan
  • 113,358
  • 34
  • 211
  • 239
  • This doesn't work from my experience, it might be a bug though: https://github.com/FasterXML/jackson-databind/issues/254 – lisak Jul 01 '13 at 08:03
  • Referenced bug had other issue(s) -- generic type resolution as shown above will work, but does not address other possible problems. – StaxMan Jul 02 '13 at 18:07
  • Yeah, my apologies, it's been resolved in here http://stackoverflow.com/questions/17400850/is-jackson-really-unable-to-deserialize-json-into-a-generic-type – lisak Jul 02 '13 at 21:23
  • What about if I have several generic types like ``` class MyClass { T1 someValue; T2 someValue2; } ``` – maks1m Apr 24 '19 at 14:51
  • @maks1m Then you would need separate methods by number of parameters – StaxMan Apr 24 '19 at 17:23
  • @StaxMan do you mean like constructParametricType(Json.class, clazz1, clazz2) ? – maks1m Apr 24 '19 at 19:20
10

Sample generic deserializing interface:

public interface Deserializable<T> {
    static final ObjectMapper mapper = new ObjectMapper();

    @SuppressWarnings("unchecked")
    default T deserialize(String rawJson) throws IOException {
        return mapper.readValue(rawJson, (Class<T>) this.getClass());
    }
}
Jose Alban
  • 7,286
  • 2
  • 34
  • 19
  • I like that answer, at least for my case. I have many classes and they all should have a read-from-json method. Therefore I wrote a base class with the type R and in the read class I stumbled across the same problem: readValue(file, R.class)??? Of course this does not work because the type is erased during compile so what to do? this.getClass() was the obvious answer. – Werner Daehn Dec 07 '19 at 07:23
2

You're serializing and deserializing Class object to JSON? Maybe keep it as String in Hit and create additional getter that launches Class.forName, e.g.

public class Hit {
    public String id;
    public String data;
    public Class<?> getDataClass() throws Exception {
       return Class.forName(data);
    }
}
Piotr Gwiazda
  • 12,080
  • 13
  • 60
  • 91
  • 1
    The error you got says: "I want to instatinate new Class object but I can't execute `new Class()` because the constructor is private". What is more it makes no sense. Please put a sample JSON string. It will be much easier to suggest a solution then. I'm pretty sure that serializing/deserializing property of type Class is a wrong path. – Piotr Gwiazda Jul 27 '12 at 10:33
2

For me something like this has worked in similar case

public <T> T jsonToObject(String jsonContainsObject, Class<T> objectType) {
       ObjectMapper objectMapper = new ObjectMapper();
       try {
         return objectMapper.readValue(jsonContainsObject, objectType);
       } catch (JsonProcessingException e) {
         //Your exception handling
       }
    return null; 
}
Piotr Żak
  • 2,083
  • 6
  • 29
  • 42
1

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 Json 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.

public class Json<T extends Result> {
    public Hits<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

Deserialize Generic class variable

...

how do I tell it to Jackson? Will gson do any better?

The Gson user guide includes a section on exactly what I understand you're trying to accomplish, though that documented example may still be incomplete.

In a blog post, I covered in more detail the solution using Gson 1.7.1. Below is the relevant code example.

Similar (but more involved) solutions using Jackson (1.8.2) are also demonstrated and described in the same blog post. (The different approaches and examples use hundreds of lines of code, so I've omitted reposting them here.)

public class GsonInstanceCreatorForParameterizedTypeDemo  
{  
 public static void main(String[] args)  
 {  
  Id<String> id1 = new Id<String>(String.class, 42);  
  
  Gson gson = new GsonBuilder().registerTypeAdapter(Id.class,  
    new IdInstanceCreator()).create();  
  String json1 = gson.toJson(id1);  
  System.out.println(json1);  
  // actual output: {"classOfId":{},"value":42}  
  // This contradicts what the Gson docs say happens.  
  // With custom serialization, as described in a  
  // previous Gson user guide section,   
  // intended output may be   
  // {"value":42}  
  
  // input: {"value":42}  
  String json2 = "{\"value\":42}";  
  
  Type idOfStringType=new TypeToken<Id<String>>(){}.getType();  
  Id<String> id1Copy = gson.fromJson(json2, idOfStringType);  
  System.out.println(id1Copy);  
  // output: classOfId=class java.lang.String, value=42  
  
  Type idOfGsonType = new TypeToken<Id<Gson>>() {}.getType();  
  Id<Gson> idOfGson = gson.fromJson(json2, idOfGsonType);  
  System.out.println(idOfGson);  
  // output: classOfId=class com.google.gson.Gson, value=42  
 }  
}  
  
class Id<T>  
{  
 private final Class<T> classOfId;  
 private final long value;  
  
 public Id(Class<T> classOfId, long value)  
 {  
  this.classOfId = classOfId;  
  this.value = value;  
 }  
  
 @Override  
 public String toString()  
 {  
  return "classOfId=" + classOfId + ", value=" + value;  
 }  
}  
  
class IdInstanceCreator implements InstanceCreator<Id<?>>  
{  
 @SuppressWarnings({ "unchecked", "rawtypes" })  
 public Id<?> createInstance(Type type)  
 {  
  Type[] typeParameters =   
    ((ParameterizedType) type).getActualTypeArguments();  
  Type idType = typeParameters[0];  
  return new Id((Class<?>) idType, 0L);  
 }  
} 
Community
  • 1
  • 1
Programmer Bruce
  • 64,977
  • 7
  • 99
  • 97