
本文介绍使用 jackson 库将 java 对象列表优雅、健壮地转换为符合嵌套结构要求的 json 数据,避免手动拼装 map 和 objectnode,提升可读性、可维护性与类型安全性。
本文介绍使用 jackson 库将 java 对象列表优雅、健壮地转换为符合嵌套结构要求的 json 数据,避免手动拼装 map 和 objectnode,提升可读性、可维护性与类型安全性。
在实际开发中,将一组领域对象(如 ObjectVO)聚合、分组并序列化为特定结构的 JSON 是常见需求。原始实现中使用 MultiValueMap<String, Map<...>> 手动构造中间映射,再逐层构建 ObjectNode 与 ArrayNode,不仅代码冗长、易出错(如字段名拼写错误、类型强转风险、空指针隐患),还严重违背面向对象设计原则——丢失了编译期类型检查和语义表达能力。
更优解是:定义清晰的 DTO 结构 + 利用 Jackson 的序列化能力 + 合理分组逻辑。以下是推荐实现步骤:
✅ 第一步:定义目标 JSON 对应的 Java 类型(推荐使用 Record 或 Lombok 简化)
// 表示 input 数组中的每个子项
public record InputItem(
@JsonProperty("id") Long idOrigin,
@JsonProperty("state") String state,
@JsonProperty("local") String local,
@JsonProperty("number") Long number
) {}
// 表示顶层 JSON 对象(对应每个唯一 id + observation 组合)
public record GroupedItem(
@JsonProperty("id") Long id,
@JsonProperty("observation") String observation,
@JsonProperty("input") List<InputItem> input
) {}? 提示:使用 @JsonProperty 显式声明字段映射,避免因命名差异(如 code → "observation")导致序列化失败;Java 14+ 推荐 record,兼顾不可变性与简洁性。
✅ 第二步:按业务键(如 id + code)对原始 ObjectVO 列表分组
List<ObjectVO> voList = ...; // 原始输入列表
// 按 id 和 code 分组(注意:code 需映射为 observation)
Map<String, List<ObjectVO>> grouped = voList.stream()
.collect(Collectors.groupingBy(
vo -> vo.getId() + "_" + vo.getCode(), // 复合键确保唯一性
LinkedHashMap::new,
Collectors.toList()
));✅ 第三步:转换为 GroupedItem 列表,并交由 Jackson 序列化
ObjectMapper mapper = new ObjectMapper();
mapper.enable(SerializationFeature.INDENT_OUTPUT); // 格式化输出(可选)
List<GroupedItem> result = grouped.entrySet().stream()
.map(entry -> {
String[] parts = entry.getKey().split("_");
Long id = Long.parseLong(parts[0]);
String observation = parts[1];
List<InputItem> inputItems = entry.getValue().stream()
.map(vo -> new InputItem(
vo.getOriginId(), // 注意字段名:Origin ID → idOrigin
vo.getState(),
vo.getLocal(),
vo.getNumber()
))
.toList();
return new GroupedItem(id, observation, inputItems);
})
.toList();
// 一行完成 JSON 序列化
String jsonOutput = mapper.writeValueAsString(result);
System.out.println(jsonOutput);⚠️ 关键注意事项
- 字段映射一致性:原始数据中 code 字段需作为 "observation" 输出,Origin id → "id"(在 InputItem 中),务必通过 @JsonProperty 或构造器参数名严格对齐;
- 空值与异常处理:生产环境建议包装 writeValueAsString() 调用,捕获 JsonProcessingException;
- 性能考量:若数据量极大(>10万条),可考虑流式 JsonGenerator 替代内存中构建 List<GroupedItem>,但绝大多数场景无需过早优化;
- 避免反模式:不推荐直接操作 ObjectNode/ArrayNode 构建复杂结构——它绕过了类型系统,调试困难且难以单元测试。
✅ 总结
相比原始基于 MultiValueMap 和手动 ObjectNode 拼接的方案,本方法优势显著:
- ✅ 类型安全:编译期校验字段存在性与类型;
- ✅ 可读性强:结构即契约,DTO 清晰表达 JSON Schema;
- ✅ 易于测试:可独立验证分组逻辑、DTO 构造逻辑与序列化结果;
- ✅ 可扩展性好:新增字段只需更新 DTO 和映射逻辑,无需重构整个 JSON 构建流程。
Jackson 的核心价值不在于“能解析任意 JSON”,而在于让 Java 类型成为 JSON 结构的一等公民——拥抱它,而非绕过它。


















