Jackson YAML反序列化时如何正确声明泛型类
Jackson YAML 泛型集合反序列化实现指南
问题核心根因
反序列化后列表元素全为LinkedHashMap的问题,本质是Java泛型擦除机制导致的:
- 运行时的Class对象会完全丢失所有泛型参数信息,代码里强转得到的
(Class<List<Contact>>)、直接传入ArrayList.class的写法,对Jackson来说只能识别出目标是一个List结构,完全无法得知List内部要存放Contact类型的对象 - Jackson在无法明确元素具体类型时,会默认使用通用的LinkedHashMap承接所有对象字段,自然没法生成预期的
Contact、PhoneNumber类型实例 - 代码中
contacts1.getClass()拿到的就是ArrayList本身的class对象,强转操作不会带来任何额外的类型信息。
零冗余的泛型反序列化实现
Jackson专门为泛型擦除场景提供了TypeReferenceAPI,不需要额外编写外层封装类,就能完整保留泛型信息实现正确映射,核心改动集中在反序列化的传参环节:
ObjectMapper yamlMapper1 = new ObjectMapper(new YAMLFactory()); List<Contact> contacts; try { contacts = yamlMapper1.readValue( new FileInputStream("C:/Temp/contacts.yaml"), // 匿名子类会在运行时保留完整泛型签名,告知Jackson这是元素为Contact的列表 new TypeReference<List<Contact>>() {} ); } catch (Exception e) { e.printStackTrace(); }
这种实现的适配性:
- 完全兼容最初设计的“纯Contact列表”YAML格式,不需要改动原有序列化逻辑,生成的YAML不需要额外嵌套
contacts根字段 - 会自动级联识别
Contact类内部的numbers列表元素为PhoneNumber类型,不需要额外配置映射规则 - 适配现有类的结构:因为没有给号码列表写set方法,Jackson会复用类初始化时已经创建的
numbers列表对象,逐元素反序列化后通过现有逻辑填充,不会因为缺少set方法出现注入失败。
外层封装类方案的适用性评估
你之前实现的“新建外层Contacts类封装ArrayList
- 优点:实现逻辑直白,不需要记忆Jackson的特殊API,对YAML格式的长期扩展兼容性好(比如后续需要加文件版本、导出时间等元数据字段时,不需要大改结构)
- 缺点:和最初定义的纯列表YAML格式不兼容,会强制让YAML多嵌套一层无意义的
contacts根节点,增加文件结构冗余;如果业务场景就是要存储纯列表格式的YAML,这个方案会额外增加格式转换的成本。
内容的提问来源于stack exchange,提问作者tusht
相关产品推荐
相关产品推荐

