封装一个转指定实体类 list 的方法,有如下两种
public static <T> List<T> parseList(String jsonString, Class<T> elementClazz) throws Exception {
ObjectMapper mapper = new ObjectMapper();
TypeReference<List<T>> typeReference = new TypeReference<List<T>>() {};
return mapper.readValue(jsonString, typeReference);
}
public static <T> List<T> parseList(String jsonString, TypeReference<List<T>> typeReference) throws Exception {
ObjectMapper mapper = new ObjectMapper();
return mapper.readValue(jsonString, typeReference);
}
public static void main(String[] args) throws Exception {
String jsonListStr = "[{\"username\":\"pure1\",\"phone\":\"18xxx\"},{\"username\":\"pure2\",\"phone\":\"19xxx\"}]";
List<User> userList1 = parseList(jsonListStr, User.class);
List<User> userList2 = parseList(jsonListStr, new TypeReference<List<User>>(){});
}
两个转完的 list 里,第一个 list 里的对象在断点里看实际上是个LinkedHashMap
,是无法正常调用实体类的 get 方法的。第二个 list 里的对象就是真正的User
。所以我分别去看了两个方法的 typeReference 对象,第一个方法的 typeReference 对象里的_type 值为“java.util.List<T>
”,第二个方法里的 typeReference 对象里的_type 值为“java.util.List<xxx.xxx.entity.User>
”。虽然第二个方法可以正常使用,但是封装肯定是为了简便,以TypeReference<List<T>> typeReference
作为入参感觉很奇怪,我底层了解的不多,我的认知里在入参的时候 new 一个 TypeReference 和在方法里 new 一个 TypeReference 应该是一样的才对。希望有大牛帮我解惑,或者是不是我第一个方法的代码写的有问题。
在此先谢谢各位了!!!
![]() |
1
xtreme1 22 天前
JavaType listType = mapper.getTypeFactory().constructCollectionType(List.class, elementClazz);
return mapper.readValue(jsonString, listType); 第一个 T 运行时被擦除了, 单步一下 TypeReference 的构造方法就知道了. |
2
visper 22 天前
看起来像是 java 的类型擦除然后第一个方法在里面 new 的时候,里面无法再知道那个是什么类型。
|
3
raylax7 22 天前
第一种会类型擦除
第二种 new TypeReference<List<User>>() {} 会生成一个单独的类文件 Class$1.class class Class$1 extends TypeReference<java.util.List<com.example.User>> { } 类型签名保留了 List<User> 的完整信息 调用 getClass().getGenericSuperclass(),就能拿到这个签名,然后解析出 User |
4
dcsuibian 22 天前
Java 在编译时会进行类型擦除,大多数会被擦掉。但继承结构中的一部分泛型信息会保存,可以通过反射读到。
|
5
lemondev 22 天前 ![]() 第一个 parseList 方法有问题,虽然 typeReference 是 TypeReference<List<T>>,
但是在运行时,T 已经被擦除成 Object 。Jackson 是不知道的,他得到了一个 Object 。具体你可以看编译后的字节码。可以看出来端倪。 这种情况你必须手动构造泛型类型了。 public static <T> List<T> parseList(String jsonString, Class<T> elementClazz) throws Exception { ObjectMapper mapper = new ObjectMapper(); JavaType javaType = mapper.getTypeFactory() .constructCollectionType(List.class, elementClazz); return mapper.readValue(jsonString, javaType); } |
6
hapeman 22 天前
java 的泛型是假泛型,只在编译期有效,编译完成后会所有泛型都会被擦除
|
![]() |
10
xuanbg 22 天前
我是这样写的:
public static <T> List<T> toList(String json, Class<T> type) { try { return MAPPER.readValue(json.trim(), getJavaType(List.class, type)); } catch (IOException ex) { throw new BusinessException(ex.getMessage()); } } |
![]() |
12
looveh 22 天前
最近刚解决这个问题就刷到你这个问题😐
我是 Postgresql 查询的时候把主数据和子数据一次性查询,子数据使用 json 数组作为子数据的一列,然后自定义一个 TypeHandler 将 json 数组转成对象集合; ```java @MappedJdbcTypes(value = JdbcType.VARCHAR) public class CustomListTypeHandler<T> extends AbstractJsonTypeHandler<List<T>> { private static final ObjectMapper OBJECT_MAPPER = new ObjectMapper(); private final Class<T> elementType; public CustomListTypeHandler(Class<T> elementType) { this.elementType = elementType; } @Override protected List<T> parse(String json) { try { CollectionType collectionType = OBJECT_MAPPER.getTypeFactory().constructCollectionType(List.class, elementType); return OBJECT_MAPPER.readValue(json, collectionType); } catch (JsonProcessingException e) { throw new RuntimeException(e); } } @Override protected String toJson(List<T> obj) { try { return OBJECT_MAPPER.writeValueAsString(obj); } catch (JsonProcessingException e) { throw new RuntimeException(e); } } } ``` |
14
pointerman 22 天前 ![]() 你这样写,每次调用方法都要 new 一个 ObjectMapper ,应该把 ObjectMapper 注入 Spring 容器,然后在工具类中 @Autowired 一个静态的 objectMapper 对象,所有工具类里的所有方法都用这个对象
|
![]() |
15
siweipancc 21 天前 via iPhone
虽然大伙讨厌八股文,但是在这个场景八股文还是有点用的。
|
![]() |
16
shiloh595 17 天前 via Android
🐮
|