This article is participating in the Java Theme Month – Java Debug Notes Event, see the event link for details

Jackson explains the correct way to write generics

This issue of 4YE has brought a small bug, as follows, have you seen this exception?

in unnamed module of loader 'app'

The bug description

The following error occurred the last time Jackson was used for data conversion.

java.lang.ClassCastException: class java.util.LinkedHashMap cannot be cast to class com.example.demo.model.User (java.util.LinkedHashMap is in module java.base of loader 'bootstrap'; com.example.demo.model.User is in unnamed module of loader 'app')
Copy the code

The error code is as follows:

  1. The first way to write it
 ObjectMapper objectMapper = new ObjectMapper();

  ResultDTO<Object> resultDTO = objectMapper.readValue(body, ResultDTO.class);

  List<String> data = (List<String>)resultDTO.getData();

  List<User> list = objectMapper.convertValue(data, new TypeReference<List<User>>() { });

  System.out.println(data);  

  System.out.println(list);

  System.out.println(list.get(0)); *// There is an error *
Copy the code
  1. The second way to write it
 ObjectMapper objectMapper = new ObjectMapper();

  Map<String, List<User>> map = objectMapper.readValue(body, Map.class);

  List<User> data = map.get("data");

  User user = data.get(0);
Copy the code

The body is the data obtained from the server in the format {“data”: [{},{}]}

It is also normal to convert to resultDTO and get the corresponding data. It is also normal to convert to list!

Then came the bug! Error: 😱 : get an element from a list 🙃🙄 : get an element from a list 🙃🙄 : get an element from a list

I have no choice but to open the debug path ~

ResultDTOThe code is as follows:

*/ * * * * * * *@author* *RYZEYANG*

 ** @date2020/10/24 name * * * /*

@Data

public class ResultDTO<T> {*/*** ** Description *** /*

  private String msg;

  */*** ** status code *** /*

  private String code;

  */*** ** data *** /*

  private T data;



}
Copy the code

The solution

Suddenly thought of myself before also done json parsing ah, that will be very smooth solution 🐖

I don’t want to get the data of that node in one step, and then convert it. And it worked!

Plan a

The code is as follows:

  ObjectMapper objectMapper = new ObjectMapper();

  JsonNode jsonNode = objectMapper.readTree(body);

  System.out.println(jsonNode);

  JsonNode data = jsonNode.get("data");



  System.out.println(data);

  JavaType javaType = objectMapper.getTypeFactory().constructParametricType(List.class, User.class);

  List<User> users = objectMapper.readValue(data.toString(), javaType);
Copy the code

The first way to write error code for error analysis

Ojectmapper.readvalue (body, resultdto.class); There e is no way to define the type of the generic T. So Jackson uses the LinkedHashMap to store parsed data 🐖

The second way to write error code for error analysis

objectMapper.readValue(body, Map.class); Here, too, there is no way to specify the generic 🐖, so Jackson uses the LinkedHashMap instead to store parsed data

Analysis here is a bit of savvy! How to add generics to ah! 🐖

The correct way to write generics is as follows:

 ResultDTO<List<User>> listResultDTO = objectMapper.readValue(body, new TypeReference<ResultDTO<List<User>>>(){});
Copy the code

Debug check, finally correct!! 😁

conclusion

Jaskson does two things when parsing the use of generic classes

  1. 🐖 :
  • ReadTree () : jsonNode jsonNode = objectMapper.readtree (body);

  • Jsonnode. get(“data”);

  • ReadValue ():objectMapper.readValue(data.tostring (), new TypeReference >(){}); The corresponding data is obtained by parsing

  1. 🐖 :
  • objectMapper.readValue(body, new TypeReference<ResultDTO<List<User>>>(){});In one step.

I am 4ye and we will see you next time ヾ( ̄▽ ̄)Bye Bye