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
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);
}
}
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.
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());
}
}
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);
}
}
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));
}