
本文介绍如何使用 jackson 库优雅地将对象列表按 id 分组、聚合并序列化为符合业务需求的嵌套 json 结构,避免冗余 map 操作,提升可读性与可维护性。
本文介绍如何使用 jackson 库优雅地将对象列表按 id 分组、聚合并序列化为符合业务需求的嵌套 json 结构,避免冗余 map 操作,提升可读性与可维护性。
在实际开发中,常需将一组扁平的对象(如 ObjectVO)按某个字段(如 id)分组,并生成结构清晰、嵌套合理的 JSON 输出。原始实现中使用 MultiValueMap
更优解是面向领域建模 + Jackson 声明式序列化。我们首先定义两个 POJO 类,精准对应目标 JSON 结构:
public static class GroupedItem {
private long id;
private String observation; // 对应原 data 中的 code 字段
private List<inputitem> input = new ArrayList();
// 构造器、getter/setter 省略(Lombok @Data 可自动补全)
}
public static class InputItem {
private long id; // 对应 Origin ID
private String state; // 保持原值
private String local; // 保持原值
private long number; // 保持原值
// 构造器、getter/setter 省略
}</inputitem>
接着,使用 Java 8+ Stream API 进行函数式分组与转换,完全规避手动 Map 操作:
// Step 1: 按 id 分组 → Map<long list>>
Map<long list>> grouped = voList.stream()
.collect(Collectors.groupingBy(ObjectVO::getId));
// Step 2: 转换为 GroupedItem 列表(每个 group 生成一个)
List<groupeditem> result = grouped.entrySet().stream()
.map(entry -> {
GroupedItem item = new GroupedItem();
item.setId(entry.getKey());
// observation 取第一个元素的 code(同 id 下 code 应一致,否则需业务校验)
item.setObservation(entry.getValue().get(0).getCode());
List<inputitem> inputs = entry.getValue().stream()
.map(vo -> {
InputItem in = new InputItem();
in.setId(vo.getOriginId()); // 注意:原始数据中字段名为 "Origin id",POJO 中建议统一为 originId
in.setState(vo.getState());
in.setLocal(vo.getLocal());
in.setNumber(vo.getNumber());
return in;
})
.collect(Collectors.toList());
item.setInput(inputs);
return item;
})
.collect(Collectors.toList());
// Step 3: 使用 Jackson 一行序列化(自动处理嵌套、null、日期等)
ObjectMapper mapper = new ObjectMapper();
mapper.enable(SerializationFeature.INDENT_OUTPUT); // 启用格式化输出
String jsonOutput = mapper.writeValueAsString(result);</inputitem></groupeditem></long></long>
✅ 优势总结:
- 类型安全:编译期检查字段与类型,杜绝 ClassCastException 和 "idOrigin" vs "idorigin" 等拼写错误;
- 可读性强:逻辑直白——分组 → 映射 → 序列化,无 ObjectNode/ArrayNode 底层操作;
- 易于扩展:新增字段只需更新 POJO 与映射逻辑,无需重写 JSON 构建代码;
- 健壮性高:ObjectMapper 自动处理 null 值、特殊字符转义、时区等边缘场景;
- 性能良好:Jackson 是业界标准高性能 JSON 库,远优于手动字符串拼接或反射遍历。
⚠️ 注意事项:
- 确保 ObjectVO 的 getter 方法命名规范(如 getOriginId() 而非 getIdOrigin()),否则需用 @JsonProperty("Origin ID") 注解标注;
- 若同一 id 下 code 不一致,应在分组后添加校验逻辑(如 Collectors.collectingAndThen(..., list -> { if (list.stream().map(...).distinct().count() > 1) throw ... }));
- 生产环境建议复用 ObjectMapper 实例(它是线程安全的),避免频繁创建开销。
通过此方式,你不再需要纠结“用 List 还是 Map 存中间数据”,而是让数据结构本身说话——清晰、稳定、可演化。
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











