Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Jackson - Deserialize Generic class variable

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

like image 389
gnjago Avatar asked Jul 25 '12 22:07

gnjago


4 Answers

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));
}
like image 97
StaxMan Avatar answered Oct 04 '22 09:10

StaxMan


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());
    }
}
like image 36
Jose Alban Avatar answered Oct 04 '22 10:10

Jose Alban


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);
    }
}
like image 45
Piotr Gwiazda Avatar answered Oct 04 '22 09:10

Piotr Gwiazda


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.

like image 43
Sanjeev Sachdev Avatar answered Oct 04 '22 11:10

Sanjeev Sachdev