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

GSON使用TypeAdapter自定义反序列化Dto的优化方案咨询

GSON自定义反序列化优化方案

问题说明

现有代码可正常运行,目标是将如下JSON字符串:

{"d": [{"d1": "D1"}, {"d2": "D2"}]}

通过GSON及自定义TypeAdapter<Dto>反序列化为Dto类型对象。当前手写的DtoAdapter需要手动维护JSON节点的开始/结束标记,逻辑繁琐,以下提供更简洁的实现方式,以及其他GSON自定义反序列化方案。

原有实现代码

public class Main {
  public static void main(String[] args) {
    GsonBuilder builder = new GsonBuilder();
    builder.registerTypeAdapter(Dto.class, new DtoAdapter());
    Gson gson = builder.create();

    String json = "{\"d\": [{\"d1\": \"D1\"}, {\"d2\": \"D2\"}]}";

    Dto dto = gson.fromJson(json, Dto.class);
    System.out.println(dto); // Dto(items=[Dto.Item(name=D1 custom case 1), Dto.Item(name=D2 custom case 2)])
  }
}

class DtoAdapter extends TypeAdapter<Dto> {
  @Override
  public Dto read(JsonReader reader) throws IOException {
    Dto dto = new Dto();

    reader.beginObject(); // Start JSON string {...}
    while (reader.hasNext()) {
      switch (reader.nextName()) {
        case "d":
          reader.beginArray(); // Start Array {"d": [...]}
          while (reader.hasNext()) {
            reader.beginObject(); // Start Object in Array  {"d": [{...}, ]}
            Dto.Item item = new Dto.Item();
            switch (reader.nextName()) {
              case "d1":
                item.setName(reader.nextString() + " custom case 1");
                dto.getItems().add(item);
                break;
              case "d2":
                item.setName(reader.nextString() + " custom case 2");
                dto.getItems().add(item);
                break;
            }
            reader.endObject(); // End Object in Array
          }
          reader.endArray(); // End Array
          break;
      }
    }
    reader.endObject(); // End JSON string

    return dto;
  }

  @Override
  public void write(JsonWriter writer, Dto dto) throws IOException {
    // Serialize
  }
}

@Data
public class Dto {
  List<Item> items = new ArrayList<>();

  @Data
  public static class Item {
    private String name;
  }
}

优化方案

方案1:简化流式TypeAdapter实现

原有TypeAdapter繁琐的核心原因是手动调用beginObject/endObject/beginArray/endArray维护嵌套层级,实际上可以在TypeAdapter内部委托默认GSON实例将当前流节点直接转为JsonElement树结构,跳过手动维护边界的步骤,同时保留流式解析的高性能优势:

class DtoAdapter extends TypeAdapter<Dto> {
    private static final Gson defaultGson = new Gson();

    @Override
    public Dto read(JsonReader reader) throws IOException {
        JsonObject root = defaultGson.fromJson(reader, JsonObject.class);
        Dto dto = new Dto();
        JsonArray dArray = root.getAsJsonArray("d");

        for (JsonElement element : dArray) {
            JsonObject itemObj = element.getAsJsonObject();
            Dto.Item item = new Dto.Item();
            for (Map.Entry<String, JsonElement> entry : itemObj.entrySet()) {
                String key = entry.getKey();
                String val = entry.getValue().getAsString();
                if ("d1".equals(key)) {
                    item.setName(val + " custom case 1");
                } else if ("d2".equals(key)) {
                    item.setName(val + " custom case 2");
                }
                dto.getItems().add(item);
            }
        }
        return dto;
    }

    @Override
    public void write(JsonWriter writer, Dto dto) throws IOException {
        // 序列化逻辑按需实现
    }
}

方案2:使用JsonDeserializer树模型接口

GSON提供了基于树模型的JsonDeserializer接口,不需要操作底层JsonReader流,所有JSON节点会被提前解析为内存中的JsonElement结构,直接按键取值即可,代码更简洁:

class DtoDeserializer implements JsonDeserializer<Dto> {
    @Override
    public Dto deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException {
        Dto dto = new Dto();
        JsonArray dArray = json.getAsJsonObject().getAsJsonArray("d");
        for (JsonElement element : dArray) {
            JsonObject itemObj = element.getAsJsonObject();
            Dto.Item item = new Dto.Item();
            if (itemObj.has("d1")) {
                item.setName(itemObj.get("d1").getAsString() + " custom case 1");
            } else if (itemObj.has("d2")) {
                item.setName(itemObj.get("d2").getAsString() + " custom case 2");
            }
            dto.getItems().add(item);
        }
        return dto;
    }
}

注册方式和TypeAdapter一致:

Gson gson = new GsonBuilder()
        .registerTypeAdapter(Dto.class, new DtoDeserializer())
        .create();

方案3:注解映射+细粒度反序列化器

通过@SerializedName注解完成JSON字段到Java字段的自动映射,只需要为存在特殊转换逻辑的Item类编写反序列化器,逻辑解耦性更好,后续维护成本更低:

  1. 修改Dto类添加字段映射注解
    @Data
    public class Dto {
      @SerializedName("d") // 自动映射JSON中的d数组到items字段
      List<Item> items = new ArrayList<>();
    
      @Data
      public static class Item {
        private String name;
      }
    }
    
  2. 只编写Item类的反序列化逻辑
    class ItemDeserializer implements JsonDeserializer<Dto.Item> {
        @Override
        public Dto.Item deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException {
            JsonObject obj = json.getAsJsonObject();
            Dto.Item item = new Dto.Item();
            if (obj.has("d1")) {
                item.setName(obj.get("d1").getAsString() + " custom case 1");
            } else if (obj.has("d2")) {
                item.setName(obj.get("d2").getAsString() + " custom case 2");
            }
            return item;
        }
    }
    
  3. 注册反序列化器
    Gson gson = new GsonBuilder()
            .registerTypeAdapter(Dto.Item.class, new ItemDeserializer())
            .create();
    

方案选型参考

  • 简化版流式TypeAdapter:性能最优,适合大体积JSON解析场景
  • JsonDeserializer实现:开发效率最高,代码最简洁,适合绝大多数常规业务场景
  • 注解+细粒度反序列化器:可维护性最好,适合类属性多、仅少数字段需要特殊转换的场景

内容的提问来源于stack exchange,提问作者Anne Maier

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.28 19:30:11