学习 Jackson 封装时候遇到一个泛型问题希望大牛能帮忙解惑 - V2EX
V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
puremaker
V2EX    Java

学习 Jackson 封装时候遇到一个泛型问题希望大牛能帮忙解惑

  •  
  •   puremaker 58 天前 1977 次点击
    这是一个创建于 58 天前的主题,其中的信息可能已经有所发展或是发生改变。

    封装一个转指定实体类 list 的方法,有如下两种

    public static <T&g; 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 应该是一样的才对。希望有大牛帮我解惑,或者是不是我第一个方法的代码写的有问题。

    在此先谢谢各位了!!!

    17 条回复    2025-09-09 14:59:23 +08:00
    xtreme1
        1
    xtreme1  
       58 天前
    JavaType listType = mapper.getTypeFactory().constructCollectionType(List.class, elementClazz);
    return mapper.readValue(jsonString, listType);

    第一个 T 运行时被擦除了, 单步一下 TypeReference 的构造方法就知道了.
    visper
        2
    visper  
       58 天前
    看起来像是 java 的类型擦除然后第一个方法在里面 new 的时候,里面无法再知道那个是什么类型。
    raylax7
        3
    raylax7  
       58 天前
    第一种会类型擦除
    第二种 new TypeReference<List<User>>() {} 会生成一个单独的类文件 Class$1.class
    class Class$1 extends TypeReference<java.util.List<com.example.User>> { } 类型签名保留了 List<User> 的完整信息
    调用 getClass().getGenericSuperclass(),就能拿到这个签名,然后解析出 User
    dcsuibian
        4
    dcsuibian  
       58 天前
    Java 在编译时会进行类型擦除,大多数会被擦掉。但继承结构中的一部分泛型信息会保存,可以通过反射读到。
    lemondev
        5
    lemondev  
       58 天前   1
    第一个 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, elementClzz);
    return mapper.readValue(jsonString, javaType);
    }
    hapeman
        6
    hapeman  
       58 天前
    java 的泛型是假泛型,只在编译期有效,编译完成后会所有泛型都会被擦除
    puremaker
        7
    puremaker  
    OP
       58 天前
    @lemondev 非常通透,理解了,感谢感谢
    puremaker
        8
    puremaker  
    OP
       58 天前
    @hapeman 受教了
    puremaker
        9
    puremaker  
    OP
       58 天前
    @raylax7 大概能懂了
    xuanbg
        10
    xuanbg  
       58 天前
    我是这样写的:
    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());
    }
    }
    xuanbg
        11
    xuanbg  
       58 天前
    @xuanbg 实际上 getJavaType 方法就是 5 楼一样的代码
    looveh
        12
    looveh  
       57 天前
    最近刚解决这个问题就刷到你这个问题
    我是 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);
    }
    }
    }
    ```
    looveh
        13
    looveh  
       57 天前
    @looveh 话说评论怎么嵌入代码?不支持 md 么
    pointerman
        14
    pointerman  
       57 天前   1
    你这样写,每次调用方法都要 new 一个 ObjectMapper ,应该把 ObjectMapper 注入 Spring 容器,然后在工具类中 @Autowired 一个静态的 objectMapper 对象,所有工具类里的所有方法都用这个对象
    siweipancc
        15
    siweipancc  
       57 天前 via iPhone
    虽然大伙讨厌八股文,但是在这个场景八股文还是有点用的。
    shiloh595
        16
    shiloh595  
       53 天前 via Android
    puremaker
        17
    puremaker  
    OP
       30 天前
    @pointerman 不注入 spring 容器也有好处,调用的时候可以直接以静态方法调用,注入了的话,工具类也得注册成组件,调用还得再类里再 @resource 一下。然后就是封装的方法可以更多样,比如说封装了“全量转为 json 字符串”和“忽略空值转为 json 字符串”,后者就是对 objectMapper 的设置多了 setSerializationInclusion(JsonInclude.Include.NON_NULL),如果用同一个 ObjectMapper 的话,就没办法同时实现这两个方法啦。是这么个道理吧?不知道我解释的对不对
    关于     帮助文档     自助推广系统     博客     API     FAQ     Solana     859 人在线   最高记录 6679       Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 25ms UTC 21:41 PVG 05:41 LAX 14:41 JFK 17:41
    Do have faith in what you're doing.
    ubao snddm index pchome yahoo rakuten mypaper meadowduck bidyahoo youbao zxmzxm asda bnvcg cvbfg dfscv mmhjk xxddc yybgb zznbn ccubao uaitu acv GXCV ET GDG YH FG BCVB FJFH CBRE CBC GDG ET54 WRWR RWER WREW WRWER RWER SDG EW SF DSFSF fbbs ubao fhd dfg ewr dg df ewwr ewwr et ruyut utut dfg fgd gdfgt etg dfgt dfgd ert4 gd fgg wr 235 wer3 we vsdf sdf gdf ert xcv sdf rwer hfd dfg cvb rwf afb dfh jgh bmn lgh rty gfds cxv xcv xcs vdas fdf fgd cv sdf tert sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf shasha9178 shasha9178 shasha9178 shasha9178 shasha9178 liflif2 liflif2 liflif2 liflif2 liflif2 liblib3 liblib3 liblib3 liblib3 liblib3 zhazha444 zhazha444 zhazha444 zhazha444 zhazha444 dende5 dende denden denden2 denden21 fenfen9 fenf619 fen619 fenfe9 fe619 sdf sdf sdf sdf sdf zhazh90 zhazh0 zhaa50 zha90 zh590 zho zhoz zhozh zhozho zhozho2 lislis lls95 lili95 lils5 liss9 sdf0ty987 sdft876 sdft9876 sdf09876 sd0t9876 sdf0ty98 sdf0976 sdf0ty986 sdf0ty96 sdf0t76 sdf0876 df0ty98 sf0t876 sd0ty76 sdy76 sdf76 sdf0t76 sdf0ty9 sdf0ty98 sdf0ty987 sdf0ty98 sdf6676 sdf876 sd876 sd876 sdf6 sdf6 sdf9876 sdf0t sdf06 sdf0ty9776 sdf0ty9776 sdf0ty76 sdf8876 sdf0t sd6 sdf06 s688876 sd688 sdf86