
本文介绍在无法修改原始 json 结构的前提下,使用 gson 将多个具有相同 `"action"` 值的对象聚合为单个对象,并将其 `"myobject"` 字段合并为 `list` 的完整实现方案。
在实际 JSON 数据集成场景中,常会遇到结构“不规范”但又不可修改的输入源——例如本例中,输入是一个 JSON 对象数组,每个元素都包含独立的 "action" 和 "myObject" 字段;而业务期望的输出却是按 "action" 分组、将所有对应 "myObject" 合并为数组的扁平化结构。由于 @JsonUnwrapped、@JsonAlias 或 ACCEPT_SINGLE_VALUE_AS_ARRAY 等标准反序列化配置无法直接解决跨对象聚合问题,我们必须采用“解析 → 转换 → 序列化”的两阶段处理策略。
✅ 推荐实现:分步建模 + 手动聚合
首先定义三类清晰职责的 POJO:
// 表示原始 JSON 数组中每个元素的结构(注意:JSON 中 key 无引号,实际解析时需确保输入为合法 JSON,即 "myObject" 和 "action" 应带双引号)
static class OriginalModel {
@SerializedName("action")
String action;
@SerializedName("myObject")
MyObjectData myObject;
}
static class TransformedModel {
@SerializedName("action")
String action;
@SerializedName("myObject")
List<MyObjectData> myObject;
public TransformedModel(String action, List<MyObjectData> myObject) {
this.action = action;
this.myObject = myObject;
}
}
static class MyObjectData {
@SerializedName("name")
String name;
@SerializedName("description")
String description;
}⚠️ 注意事项:若嵌套在外部类中,务必声明为 static,否则 Gson 反序列化可能因隐式引用外部实例而失败;原始 JSON 示例中 myObject: { ... } 缺少引号,实际运行前必须确保输入是标准 JSON(即 "myObject": { ... }),否则 Gson.fromJson() 将抛出 JsonSyntaxException;@SerializedName 注解显式声明字段名,增强健壮性,避免因大小写或下划线风格差异导致映射失败。
接下来执行核心聚合逻辑:
Gson gson = new Gson();
String jsonInput = "[\n" +
" {\"myObject\": {\"name\": \"foo\", \"description\": \"bar\"}, \"action\": \"create\"},\n" +
" {\"myObject\": {\"name\": \"baz\", \"description\": \"qux\"}, \"action\": \"create\"}\n" +
"]";
// Step 1: 解析为原始模型列表
List<OriginalModel> originalList = gson.fromJson(jsonInput, new TypeToken<List<OriginalModel>>() {}.getType());
// Step 2: 按 action 分组聚合 myObject(保持原始顺序)
Map<String, List<MyObjectData>> grouped = new LinkedHashMap<>();
for (OriginalModel item : originalList) {
grouped.computeIfAbsent(item.action, k -> new ArrayList<>()).add(item.myObject);
}
// Step 3: 构造转换后模型列表
List<TransformedModel> result = grouped.entrySet().stream()
.map(entry -> new TransformedModel(entry.getKey(), entry.getValue()))
.collect(Collectors.toList());
// Step 4: 序列化为最终 JSON(可选:格式化输出便于调试)
String outputJson = gson.toJson(result);
System.out.println(gson.toJson(result, TransformedModel.class)); // 输出符合预期的结构输出结果为:
立即学习“Java免费学习笔记(深入)”;
[{
"action": "create",
"myObject": [
{"name": "foo", "description": "bar"},
{"name": "baz", "description": "qux"}
]
}]? 扩展说明:Jackson 实现思路(简要)
若项目已深度绑定 Jackson,可复用相同建模逻辑,仅替换解析/序列化方式:
ObjectMapper mapper = new ObjectMapper();
List<OriginalModel> originalList = mapper.readValue(jsonInput,
new TypeReference<List<OriginalModel>>() {});
// 后续分组与构造逻辑完全一致
// …
String outputJson = mapper.writeValueAsString(result);无需额外配置 DeserializationFeature.ACCEPT_SINGLE_VALUE_AS_ARRAY 或 @JsonAnySetter——因为本需求本质是数据重组(data reshaping),而非单字段类型适配,强行依赖注解易引入歧义与维护成本。
✅ 总结
- ✅ 核心思想:将“反序列化难题”转化为“内存数据聚合问题”,解耦解析与业务逻辑;
- ✅ 健壮设计:使用 LinkedHashMap 保障分组顺序与输入一致;
- ✅ 可扩展性:如需支持多 action 值(如 "create" / "update"),当前分组逻辑天然兼容;
- ❌ 避免陷阱:不要试图用 @JsonCreator 或自定义 JsonDeserializer 一次性完成聚合——复杂度陡增且难以测试。
该方案简洁、可读性强、易于单元测试,是处理此类非标准 JSON 整合任务的专业级实践。










