“This is the 21st day of my participation in the First Challenge 2022. For details: First Challenge 2022.”

Author: Tangyuan

Personal blog: Javalover.cc

preface

Jackson parsing collections is exactly the same as parsing Java objects, only slightly different;

If the data type in the collection is primitive, such as String, then we can parse it directly, readValue(s, list.class), and output will be fine;

But if the data type in the collection is a Java class, such as User, then parsing becomes problematic because Jackson erases the class information and prints data such as Map.

Let’s do an example;

directory

  1. Parses a collection of primitive types
  2. Parse a collection of Java classes

The body of the

1. Parse a collection of primitive types

This can be done directly by looking at the code, as shown below:

List<String> list = Arrays.asList("a"."b");
ObjectMapper objectMapper = new ObjectMapper();
String s = objectMapper.writeValueAsString(list);

List<String> list1 = objectMapper.readValue(s, List.class);
System.out.println(list1);
Copy the code

The output is as follows:

As you can see, as expected, no objections;

2. Parse a collection of Java classes

Here we create a User entity class:

@Data
@AllArgsConstructor
@NoArgsConstructor
public class User {

    private String username;

    private String password;

}

Copy the code

The main program then builds multiple User objects to test:

User user = new User("jalon"."xiaowang");
User user2 = new User("jalon2"."xiaowang2");
List<User> list = Arrays.asList(user, user2);
ObjectMapper objectMapper = new ObjectMapper();
String s = objectMapper.writeValueAsString(list);

List<User> list1 = objectMapper.readValue(s, List.class);
System.out.println( list1.get(0).getUsername());
Copy the code

Here we want to display the username of the first User, but we get an error:

Problem Description:

Error: LinkedHashMap cannot be converted to User, indicating that the User attribute in the parsed list has been erased and LinkedHashMap has been replaced.

Solutions:

There are two solutions: construct TypeReference and CollectionType. The core of both methods is to wrap the collection to be serialized and then process it accordingly.

Method 1: TypeReference wrapper

TypeReference takes a generic T as a parameter. We can pass List

as an argument and then read TypeReference data as follows:

TypeReference<List<User>> typeReference = new TypeReference<List<User>>() {};
List<User> list1 = objectMapper.readValue(s, typeReference);
Copy the code

ReadValue converts TypeReference to JavaType.

As you can see, a JavaType is essentially constructed with the typeReference parameter

Then we run the following output:

As you can see, we successfully accessed the user. username property. If we printed the entire List, we would print all the User objects:

System.out.println( list1);
Copy the code

Option 2: CollectionType wrapper

The CollectionType wrapper wraps the attributes of the List collection and the User entity class separately, and then reads them, as shown below:

CollectionType collectionType = objectMapper.getTypeFactory().constructCollectionType(List.class, User.class);
List<User> list = objectMapper.readValue(s, collectionType);
Copy the code

At this point, run the output, the same result as above, is normal output;

The core of both the CollectionType wrapper and the TypeReference wrapper is to wrap collections and element types to be serialized, and then convert them when read.

conclusion

If the parsed collection element is a primitive type, it can be parsed without any processing.

If the parsed collection element is a Java class, then the collection needs to be wrapped with TypeReference or CollectionType and then read.