如何使用Java数据结构结合Gson序列化不含children属性的嵌套JSON结构?
解决Gson序列化动态嵌套子节点的Java数据结构方案
针对你需要生成的这种直接将子节点作为Person对象的顶级属性(而非通过children数组/字段嵌套)的JSON结构,这里有两个实用的方案,结合Gson就能完美实现需求:
方案一:让Person继承HashMap<String, Object>(快速实现)
这个方案利用Gson会同时序列化类的字段和Map条目的特性,让Person既保留固定的Type和age属性,又能动态添加子节点(键为孩子名字,值为Child对象)。
代码示例
首先定义实体类:
import com.google.gson.annotations.SerializedName; import java.util.HashMap; class Person extends HashMap<String, Object> { @SerializedName("Type") private final String type = "Person"; // 固定为Person类型 private int age; // 构造器和age的getter/setter public Person(int age) { this.age = age; } public int getAge() { return age; } public void setAge(int age) { this.age = age; } // 添加子节点的方法,避免键名和固定属性冲突 public void addChild(String childName, Child child) { if ("Type".equals(childName) || "age".equals(childName)) { throw new IllegalArgumentException("Child name cannot conflict with fixed fields: Type/age"); } put(childName, child); } } class Child { @SerializedName("Type") private final String type = "child"; // 固定为child类型 private int age; public Child(int age) { this.age = age; } // 可选:age的getter/setter }
然后使用Gson序列化:
import com.google.gson.Gson; import com.google.gson.GsonBuilder; import java.util.HashMap; import java.util.Map; public class Main { public static void main(String[] args) { // 构建John的节点 Person john = new Person(30); john.addChild("Sean", new Child(3)); john.addChild("Julian", new Child(4)); // 构建Paul的节点 Person paul = new Person(64); paul.addChild("Stella", new Child(10)); paul.addChild("James", new Child(12)); // 外层用Map<String, Person>处理John、Paul顶级节点 Map<String, Person> peopleMap = new HashMap<>(); peopleMap.put("John", john); peopleMap.put("Paul", paul); // 生成格式化后的JSON Gson gson = new GsonBuilder().setPrettyPrinting().create(); String jsonOutput = gson.toJson(peopleMap); System.out.println(jsonOutput); } }
方案优缺点
- ✅ 优点:实现简单,代码量少,无需额外配置Gson
- ❌ 缺点:需要注意避免子节点名字和固定字段(
Type、age)冲突;反序列化时需要注意区分固定字段和子节点
方案二:自定义TypeAdapter(精确控制序列化/反序列化)
如果你希望更严谨地控制序列化逻辑,不想让Person继承Map,可以用Gson的自定义TypeAdapter,手动处理固定字段和动态子节点的序列化/反序列化。
代码示例
首先定义常规的实体类(无需继承Map):
import com.google.gson.annotations.SerializedName; import java.util.HashMap; import java.util.Map; class Person { @SerializedName("Type") private String type = "Person"; private int age; // 用Map存储子节点,键为孩子名字 private Map<String, Child> childrenByName = new HashMap<>(); // 构造器、getter/setter public Person(int age) { this.age = age; } public String getType() { return type; } public void setType(String type) { this.type = type; } public int getAge() { return age; } public void setAge(int age) { this.age = age; } public Map<String, Child> getChildrenByName() { return childrenByName; } public void addChild(String childName, Child child) { childrenByName.put(childName, child); } } class Child { @SerializedName("Type") private String type = "child"; private int age; public Child(int age) { this.age = age; } // getter/setter public String getType() { return type; } public void setType(String type) { this.type = type; } public int getAge() { return age; } public void setAge(int age) { this.age = age; } }
然后编写自定义的TypeAdapter:
import com.google.gson.TypeAdapter; import com.google.gson.Gson; import com.google.gson.stream.JsonReader; import com.google.gson.stream.JsonWriter; import java.io.IOException; import java.util.Map; class PersonTypeAdapter extends TypeAdapter<Person> { private final Gson gson = new Gson(); @Override public void write(JsonWriter out, Person person) throws IOException { out.beginObject(); // 写入固定字段Type和age out.name("Type").value(person.getType()); out.name("age").value(person.getAge()); // 遍历子节点Map,将每个键值对作为Person的顶级属性写入 for (Map.Entry<String, Child> entry : person.getChildrenByName().entrySet()) { out.name(entry.getKey()); gson.toJson(entry.getValue(), Child.class, out); } out.endObject(); } @Override public Person read(JsonReader in) throws IOException { Person person = new Person(0); Map<String, Child> children = new HashMap<>(); in.beginObject(); while (in.hasNext()) { String fieldName = in.nextName(); switch (fieldName) { case "Type": person.setType(in.nextString()); break; case "age": person.setAge(in.nextInt()); break; default: // 其他字段都作为子节点处理 Child child = gson.fromJson(in, Child.class); children.put(fieldName, child); break; } } in.endObject(); person.getChildrenByName().putAll(children); return person; } }
最后注册TypeAdapter并序列化:
import com.google.gson.Gson; import com.google.gson.GsonBuilder; import java.util.HashMap; import java.util.Map; public class Main { public static void main(String[] args) { Person john = new Person(30); john.addChild("Sean", new Child(3)); john.addChild("Julian", new Child(4)); Person paul = new Person(64); paul.addChild("Stella", new Child(10)); paul.addChild("James", new Child(12)); Map<String, Person> peopleMap = new HashMap<>(); peopleMap.put("John", john); peopleMap.put("Paul", paul); // 注册自定义TypeAdapter Gson gson = new GsonBuilder() .registerTypeAdapter(Person.class, new PersonTypeAdapter()) .setPrettyPrinting() .create(); String jsonOutput = gson.toJson(peopleMap); System.out.println(jsonOutput); } }
方案优缺点
- ✅ 优点:逻辑清晰,完全控制序列化/反序列化过程,避免键名冲突问题,反序列化更安全
- ❌ 缺点:需要编写额外的TypeAdapter代码,相对繁琐一些
两种方案都能生成你需要的JSON结构,根据你的项目复杂度选择即可。
内容的提问来源于stack exchange,提问作者Erez Ben Harush
相关产品推荐
相关产品推荐

