How to use Jackson ObjectMapper.readValue with generic class

Viewed 107

how to use Jackson ObjectMapper.readValue with generic class, someone says that need JavaType, but JavaType is also splicing other class, is Jackson can use like gson TypeToken?

my code is like this

    public static void main(String[] args) throws IOException {
    String json = "{\"code\":200,\"msg\":\"success\",\"reqId\":\"d1ef3b76e73b40379f895a3a7f1389e2\",\"cost\":819,\"result\":{\"taskId\":1103,\"taskName\":\"ei_custom_config\",\"jobId\":233455,\"status\":2,\"interrupt\":false,\"pass\":true}}";
    RestResponse<TaskResult> result = get(json);
    System.out.println(result);
    System.out.println(result.getResult().getJobId());
}

public static <T> RestResponse<T> get(String json) throws IOException {
    ObjectMapper objectMapper = new ObjectMapper();
    return objectMapper.readValue(json, new TypeReference<RestResponse<T>>() {});
}

and error is

org.example.zk.RestResponse@6fd02e5
Exception in thread "main" java.lang.ClassCastException: java.util.LinkedHashMap cannot be cast to org.example.zk.TaskResult
    at org.example.zk.JacksonTest.main(JacksonTest.java:15)
2 Answers

You need to provide jackson with concrete type information for T. I would suggest using readValue() overload with parameter - JavaType.

Add the class of T as parameter of get() and construct parametric type using it.

public static <T> RestResponse<T> get(String json, Class<T> classOfT) throws IOException {
  ObjectMapper objectMapper = new ObjectMapper();
  JavaType type = TypeFactory.defaultInstance().constructParametricType(RestResponse.class, classOfT);
  return objectMapper.readValue(json, type);
}

Usage:

RestResponse<TaskResult> result = get(json, TaskResult.class);

We can make T with upper-bound to help infering object type.

public static <T extends TaskResult> RestResponse<T> get(String json) throws IOException {
    ObjectMapper objectMapper = new ObjectMapper();
    return objectMapper.readValue(json, new TypeReference<RestResponse<T>>() {});
}

Without type bounduary, RestResponse<T> equals to RestResponse<Object>
We can not new a generic class with T.

Related