You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Java中使用Gson处理JSON多类型children字段的规范实现方式

Hey,我完全懂你现在的困扰——用字符串替换"kids"为"children"确实是个临时凑活的办法,但正如你说的,一旦数据里真的出现了"kids"这个词(比如某个节点的text是"Kids' Zone"),替换之后就彻底乱套了,而且这种方案完全不具备扩展性。

其实Gson本身就提供了完美解决这种多类型字段问题的机制,咱们直接用官方推荐的方式来搞定:

方案1:自定义Json序列化器/反序列化器

首先,咱们先把Data类里多余的kids字段删掉,只保留children列表——毕竟咱们要的是一个字段对应两种类型,不是两个字段来回替换:

public class Data {
    private String id;
    private String text;
    private String icon;
    private State state;
    private List<Data> children;

    // 所有getter、setter方法保持不变,去掉原来的kids相关方法
}

接下来写一个自定义的JsonSerializer,用来控制Data对象转JSON时的逻辑:当children为空或者null时,输出布尔值false;当有子节点时,输出数组。

public class DataSerializer implements JsonSerializer<Data> {
    @Override
    public JsonElement serialize(Data src, Type typeOfSrc, JsonSerializationContext context) {
        JsonObject jsonObject = new JsonObject();
        
        // 序列化基础字段
        jsonObject.addProperty("id", src.getId());
        jsonObject.addProperty("text", src.getText());
        if (src.getIcon() != null) {
            jsonObject.addProperty("icon", src.getIcon());
        }
        if (src.getState() != null) {
            jsonObject.add("state", context.serialize(src.getState()));
        }
        
        // 核心:处理children字段的多类型逻辑
        if (src.getChildren() == null || src.getChildren().isEmpty()) {
            jsonObject.addProperty("children", false);
        } else {
            jsonObject.add("children", context.serialize(src.getChildren()));
        }
        
        return jsonObject;
    }
}

如果还需要处理JSON转Data对象的反序列化(比如后端返回的JSON里children可能是布尔值或数组),再写一个JsonDeserializer:

public class DataDeserializer implements JsonDeserializer<Data> {
    @Override
    public Data deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException {
        JsonObject jsonObject = json.getAsJsonObject();
        Data data = new Data();
        
        // 反序列化基础字段
        data.setId(jsonObject.get("id").getAsString());
        data.setText(jsonObject.get("text").getAsString());
        if (jsonObject.has("icon")) {
            data.setIcon(jsonObject.get("icon").getAsString());
        }
        if (jsonObject.has("state")) {
            data.setState(context.deserialize(jsonObject.get("state"), State.class));
        }
        
        // 核心:处理children字段的两种类型
        JsonElement childrenElement = jsonObject.get("children");
        if (childrenElement.isJsonPrimitive() && childrenElement.getAsJsonPrimitive().isBoolean()) {
            // 如果是布尔值,这里可以根据业务需求设置children为null或空列表
            data.setChildren(null);
        } else if (childrenElement.isJsonArray()) {
            data.setChildren(context.deserialize(childrenElement, new TypeToken<List<Data>>(){}.getType()));
        }
        
        return data;
    }
}

最后,在创建Gson实例的时候注册这两个适配器:

public static String createFolderJsonData() {
    CreateFolderTree cft = new CreateFolderTree(null);
    Data treeData = cft.start(1);
    
    Gson gson = new GsonBuilder()
            .registerTypeAdapter(Data.class, new DataSerializer())
            .registerTypeAdapter(Data.class, new DataDeserializer())
            .create();
    
    return gson.toJson(treeData);
}
方案2:使用@JsonAdapter注解(更简洁)

如果觉得每次创建Gson都要注册适配器太麻烦,可以直接用@JsonAdapter注解把适配器和Data类绑定,写一个集成读写逻辑的TypeAdapter:

首先给Data类加注解:

@JsonAdapter(DataAdapter.class)
public class Data {
    // 字段和getter/setter不变
}

然后写DataAdapter:

public class DataAdapter extends TypeAdapter<Data> {
    private final Gson gson = new Gson();
    
    @Override
    public void write(JsonWriter out, Data value) throws IOException {
        out.beginObject();
        // 写入基础字段
        out.name("id").value(value.getId());
        out.name("text").value(value.getText());
        if (value.getIcon() != null) {
            out.name("icon").value(value.getIcon());
        }
        if (value.getState() != null) {
            out.name("state");
            gson.toJson(value.getState(), State.class, out);
        }
        
        // 处理children字段
        if (value.getChildren() == null || value.getChildren().isEmpty()) {
            out.name("children").value(false);
        } else {
            out.name("children");
            gson.toJson(value.getChildren(), new TypeToken<List<Data>>(){}.getType(), out);
        }
        out.endObject();
    }
    
    @Override
    public Data read(JsonReader in) throws IOException {
        Data data = new Data();
        in.beginObject();
        while (in.hasNext()) {
            switch (in.nextName()) {
                case "id":
                    data.setId(in.nextString());
                    break;
                case "text":
                    data.setText(in.nextString());
                    break;
                case "icon":
                    data.setIcon(in.nextString());
                    break;
                case "state":
                    data.setState(gson.fromJson(in, State.class));
                    break;
                case "children":
                    JsonToken token = in.peek();
                    if (token == JsonToken.BOOLEAN) {
                        in.nextBoolean();
                        data.setChildren(null);
                    } else if (token == JsonToken.BEGIN_ARRAY) {
                        data.setChildren(gson.fromJson(in, new TypeToken<List<Data>>(){}.getType()));
                    }
                    break;
                default:
                    in.skipValue(); // 跳过未知字段,避免解析失败
                    break;
            }
        }
        in.endObject();
        return data;
    }
}

这样使用的时候直接用new Gson()就行,不需要额外注册:

public static String createFolderJsonData() {
    CreateFolderTree cft = new CreateFolderTree(null);
    Data treeData = cft.start(1);
    return new Gson().toJson(treeData);
}
为什么不推荐字符串替换?

除了你提到的“误替换真实数据中的'kids'”问题,还有这些坑:

  • 如果JSON结构复杂,嵌套层级深,字符串替换可能会破坏其他字段的格式
  • 完全不具备类型安全性,一旦业务逻辑变化(比如children需要支持第三种类型),根本没法扩展
  • 代码可读性差,其他维护者看到replace操作会一脸懵,不知道背后的业务逻辑

这种自定义适配器的方式才是Gson官方推荐的多类型字段处理方案,不仅能精准控制序列化/反序列化逻辑,还能保证代码的可维护性和扩展性。

内容的提问来源于stack exchange,提问作者Paul Taylor

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.15 06:46:25