如何使用Java与Google Gson将嵌套JSON数组UNNEST为类SQL关联查询结果
Java + Gson: 动态展开嵌套JSON数组为SQL JOIN风格的扁平结构
需求背景
你有一个包含多层嵌套数组(items、tags、actions)的JSON结构,需要将其转换为类似SQL关联查询的扁平输出——每个嵌套数组的元素单独成为一条记录,未关联的字段填充为null,同时保留顶层的公共字段(比如number)。
实现方案
我们可以通过定义数据模型 + 手动遍历转换的方式实现,Gson负责JSON的序列化和反序列化工作。以下是具体步骤:
1. 定义数据模型类
首先创建对应原始JSON结构的模型类,以及对应输出扁平结构的模型类:
import com.google.gson.annotations.SerializedName; import java.util.List; // 原始JSON对应的模型类 class Order { private int number; private List<Item> items; private List<String> tags; private Customer customer; // 该字段输出未用到,可按需扩展处理 private List<Action> actions; // Getters & Setters (可使用Lombok简化代码) public int getNumber() { return number; } public void setNumber(int number) { this.number = number; } public List<Item> getItems() { return items; } public void setItems(List<Item> items) { this.items = items; } public List<String> getTags() { return tags; } public void setTags(List<String> tags) { this.tags = tags; } public List<Action> getActions() { return actions; } public void setActions(List<Action> actions) { this.actions = actions; } } class Item { private String product; private double cost; public String getProduct() { return product; } public void setProduct(String product) { this.product = product; } public double getCost() { return cost; } public void setCost(double cost) { this.cost = cost; } } class Action { @SerializedName("moment") private String actionMoment; private String description; public String getActionMoment() { return actionMoment; } public void setActionMoment(String actionMoment) { this.actionMoment = actionMoment; } public String getDescription() { return description; } public void setDescription(String description) { this.description = description; } } class Customer { private String name; private String shortName; // Getters & Setters 省略 } // 输出扁平结构对应的模型类 class FlatOrder { private int number; @SerializedName("items_product") private String itemsProduct; @SerializedName("items_cost") private Double itemsCost; // 用包装类型允许字段为null private String tags; @SerializedName("actions_moment") private String actionsMoment; @SerializedName("actions_description") private String actionsDescription; // 构造方法:初始化公共字段number,其他字段默认null public FlatOrder(int number) { this.number = number; } // Getters & Setters 省略 public void setItemsProduct(String itemsProduct) { this.itemsProduct = itemsProduct; } public void setItemsCost(Double itemsCost) { this.itemsCost = itemsCost; } public void setTags(String tags) { this.tags = tags; } public void setActionsMoment(String actionsMoment) { this.actionsMoment = actionsMoment; } public void setActionsDescription(String actionsDescription) { this.actionsDescription = actionsDescription; } }
2. 编写转换逻辑
创建工具方法遍历原始Order对象,分别处理每个嵌套数组,生成对应的扁平记录:
import com.google.gson.Gson; import com.google.gson.reflect.TypeToken; import java.util.ArrayList; import java.util.List; public class JsonFlattener { private static final Gson gson = new Gson(); public static void main(String[] args) { // 原始JSON字符串 String originalJson = "[ \n" + "{ \n" + "\"number\": 123, \n" + "\"items\" : [ {\"product\": \"P1\",\"cost\":10.5}, {\"product\": \"P2\",\"cost\":5.25} ], \n" + "\"tags\":[\"a\",\"b\",\"c\"], \n" + "\"customer\": { \"name\": \"Roberto\", \"shortName\": \"Beto\" }, \n" + "\"actions\":[{ \"moment\": \"2021-01-01 11:22:22.222\", \"description\": \"action 1\" },{ \"moment\": \"2021-01-23 11:22:22.222\", \"description\": \"action 2\" }] \n" + "} \n" + "]"; // 反序列化原始JSON为Order列表 List<Order> orders = gson.fromJson(originalJson, new TypeToken<List<Order>>() {}.getType()); // 转换为扁平结构 List<FlatOrder> flatOrders = convertToFlatOrders(orders); // 序列化输出结果 String outputJson = gson.toJson(flatOrders); System.out.println(outputJson); } public static List<FlatOrder> convertToFlatOrders(List<Order> orders) { List<FlatOrder> result = new ArrayList<>(); for (Order order : orders) { int orderNumber = order.getNumber(); // 处理items数组:每个Item生成一条扁平记录 if (order.getItems() != null && !order.getItems().isEmpty()) { for (Item item : order.getItems()) { FlatOrder flat = new FlatOrder(orderNumber); flat.setItemsProduct(item.getProduct()); flat.setItemsCost(item.getCost()); result.add(flat); } } // 处理tags数组:每个tag生成一条扁平记录 if (order.getTags() != null && !order.getTags().isEmpty()) { for (String tag : order.getTags()) { FlatOrder flat = new FlatOrder(orderNumber); flat.setTags(tag); result.add(flat); } } // 处理actions数组:每个Action生成一条扁平记录 if (order.getActions() != null && !order.getActions().isEmpty()) { for (Action action : order.getActions()) { FlatOrder flat = new FlatOrder(orderNumber); flat.setActionsMoment(action.getActionMoment()); flat.setActionsDescription(action.getDescription()); result.add(flat); } } } return result; } }
3. 关键细节说明
- 字段映射:通过
@SerializedName注解确保输出JSON的字段名与需求完全匹配(比如items_product对应模型类的itemsProduct)。 - Null兼容:扁平模型类使用包装类型(如
Double)替代基本类型,避免基本类型无法为null的问题;未赋值的字段Gson会自动序列化为null。 - 扩展性:如果后续需要新增嵌套数组的处理,只需在转换方法中添加对应数组的遍历逻辑即可。
运行上述代码后,就能得到你需要的类似SQL JOIN风格的扁平JSON输出。
内容的提问来源于stack exchange,提问作者Beto Neto
相关产品推荐
相关产品推荐

