Jackson多层泛型深度嵌套导致的反序列化问题

问题描述:

对于一个多层嵌套泛型(深度泛型)的对象进行序列化是容易的,但是反序列化却往往不是很顺利。

//调用jackson对象
ObjectMapper mapper = new ObjectMapper();
mapper.setSerializationInclusion(Include.NON_NULL); 

//封装用户对象
AlUsers alUsers1 = new AlUsers();alUsers1.setUsername("WOW");
AlUsers alUsers2 = new AlUsers();alUsers2.setUsername("SC2");
List<AlUsers> usersList = Lists.newArrayList(alUsers1, alUsers2);

//设计的多泛型嵌套map
Map<String, List<AlUsers>> map = Maps.newHashMap();
map.put("L", usersList);

//序列化
String json = mapper.writeValueAsString(map)

我们设计了一个多层泛型嵌套的对象map,对其进行序列化,是没有问题的

//打印
{"L":[{"username":"WOW"},{"username":"SC2"}]}

我们现在使用反序列化处理

Map<String, List<AlUsers>> map1 = mapper.readValue(json, Map.class);
List<AlUsers> users = map1.get("L");
for (AlUsers user : users) {
    System.out.println(mapper.writeValueAsString(user));
}

结果抛出了异常

java.lang.ClassCastException: java.util.LinkedHashMap cannot be cast to com.flight.carryprice.entity.AlUsers

Jackson是不支持多泛型深度嵌套反序列化的,强转后的结果就是默认将AlUsers对象变成了LinkedHashMap,所以在循环的时候强转AlUsers对象就会报错

即使我在每一层都序列化一次,最后还是报错

String listStr = mapper.writeValueAsString(usersList); //内层序列化
Map<String, String> map = Maps.newHashMap();
map.put("L", listStr);
String json = mapper.writeValueAsString(map); //外层序列化

//异常
java.lang.ClassCastException: java.util.LinkedHashMap cannot be cast to com.flight.carryprice.entity.AlUsers

解决办法:

这里提供两种解决思路

1、既然问题出在循环过程中对象的强转,那么我们可以使用普通的for循环,使用get(index)方法获取对象的时候先转为json,再反序列化为AlUsers,中间倒腾一手

Map<String, List<AlUsers>> map1 = mapper.readValue(json, Map.class);
List<AlUsers> users = map1.get("L");
for (int i = 0; i < users.size(); i++) {
    
    //序列化再反序列化
    AlUsers alUsers = mapper.readValue(mapper.writeValueAsString(users.get(i)), AlUsers.class);
    System.out.println(mapper.writeValueAsString(alUsers));
}

//打印
{"username":"WOW"}
{"username":"SC2"}

2、我们使用TypeReference对象

//源码 
protected TypeReference()
 {
     Type superClass = getClass().getGenericSuperclass();
     if (superClass instanceof Class<?>) { // sanity check, should never happen
         throw new IllegalArgumentException("Internal error: TypeReference constructed without actual type information");
     }
     /* 22-Dec-2008, tatu: Not sure if this case is safe -- I suspect
      *   it is possible to make it fail?
      *   But let's deal with specific
      *   case when we know an actual use case, and thereby suitable
      *   workarounds for valid case(s) and/or error to throw
      *   on invalid one(s).
      */
     _type = ((ParameterizedType) superClass).getActualTypeArguments()[0];
 }

TypeReference对象就是通过java反省机制,获取所指定的泛型的类型,在反序列中根据指定的泛型类型进行反序列化

//通过TypeReference指定反序列化的类型
Map<String, List<AlUsers>> map1 = mapper.readValue(json, new TypeReference<Map<String, List<AlUsers>>>(){});
List<AlUsers> users = map1.get("L");
for (AlUsers user : users) {
	System.out.println(mapper.writeValueAsString(user));
}

//打印
{"username":"WOW"}
{"username":"SC2"}

版权声明:本文为qq_45337431原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接和本声明。