REST响应被识别为LinkedHashMap而非自定义对象,需转换键名大小写
1. 泛型强转无实际类型校验
Java的泛型是编译期擦除的,运行时不会对泛型的实际类型做校验。你写的(List<FurtherData>)强转代码,运行时只会检查外层对象是不是List类型,完全不会校验List内部的元素是不是FurtherData实例,哪怕内部全是Map结构,这行强转也不会抛异常。
2. 自定义类的映射逻辑完全没触发
你调用的benefits.get(FURTHER_DATA)是Map接口的原生取值方法——说明这个Benefit对象本身是基于Map结构做反序列化的,Jackson在把上游JSON转成Benefit对象的时候,根本不知道这个键对应的值要映射到你定义的FurtherData类型上,只会按照默认规则把JSON数组转成List<LinkedHashMap>,Map的键就是上游返回的原始大写字符串,值就是对应的文本内容。
这也是为什么你观察到List里的元素全是LinkedHashMap的根本原因。
3. 注解完全未生效的原因
Jackson序列化的时候,只会按照LinkedHashMap里存的原始大写键名直接输出,根本不会读取你写在FurtherData类上的@JsonProperty注解——因为运行时从来没实例化过FurtherData类的对象,注解自然完全不起作用,最终返回的键名还是上游的大写格式。
你之前看到“注释掉FurtherData的所有字段程序还能正常运行”,本质是程序从头到尾就没用到过这个类,哪怕把整个类文件删掉,也不会影响运行。
4. 关于“注释强转就没数据”的误解
你之前观察到注释掉强转代码后响应里不出现对应数据块,不是强转逻辑做了什么特殊转换,只是你后续的响应组装逻辑是从这个强转后的data变量取值的。注释掉强转后data变量没有被赋值,组装响应的时候自然就不会把对应数据块放进去,和类型转换逻辑没有关系。
1. 显式触发类型转换,替换无意义的强转
不要依赖Map强转拿到结构化对象,直接用项目中已经集成的Jackson ObjectMapper,把拿到的原始Map列表显式转成自定义类型:
// 获取原始未结构化的反序列化结果 List<?> rawDataList = (List<?>) benefits.get(FURTHER_DATA); // 显式做带泛型的类型转换,触发注解映射逻辑 List<FurtherData> data = objectMapper.convertValue( rawDataList, new TypeReference<List<FurtherData>>() {} );
这一步执行时,Jackson会读取你写在FurtherData类上的@JsonProperty注解,把Map里键为IMAGE/DESCRIPTION/HEADER的值,赋值给对应类成员变量,完成结构化转换。
2. 配置输出阶段的小写键名规则
因为你最终需要给客户端返回全小写的键名,不需要修改反序列化阶段的@JsonProperty映射(否则会匹配不到上游的大写键),只需要在FurtherData类上添加全局的序列化命名策略:
@Getter @Setter // 序列化输出时统一使用小写键名,不影响反序列化阶段的注解映射 @JsonNaming(PropertyNamingStrategies.LowerCaseStrategy.class) public class FurtherData { // 反序列化阶段匹配上游接口的大写键 @JsonProperty("IMAGE") private String image; @JsonProperty("DESCRIPTION") private String description; @JsonProperty("HEADER") private String header; }
配置完成后,Jackson序列化输出时会自动把成员变量名转成全小写格式,正好匹配你需要的面向客户端的响应结构。
3. 可选优化:分离上下游DTO
如果担心上游接口结构变动影响客户端返回,可以单独定义面向客户端的响应DTO,不要复用上流的反序列化实体类。DTO的字段直接定义成小写格式,用工具类把上游实体类的属性值拷贝过去,实现上下游结构的解耦。
内容的提问来源于stack exchange,提问作者karvai

