1. 简介

在本篇快速教程中,我们将学习如何使用 Google 的 Gson 将 JSON 字符串转换为 Map 类型对象。

我们会介绍三种不同的实现方式,并结合实际示例分析它们的优缺点。

2. 使用 Map.class 直接解析

Gson 提供了如下 API 用于将 JSON 字符串反序列化为 Java 对象:

public <T> T fromJson(String json, Class<T> classOfT) throws JsonSyntaxException;

方法签名很明确:第二个参数是目标对象的类类型。如果我们要转成 Map,自然传入的就是 Map.class

String jsonString = "{'employee.name':'Bob','employee.salary':10000}";
Gson gson = new Gson();
Map map = gson.fromJson(jsonString, Map.class);
Assert.assertEquals(2, map.size());
Assert.assertEquals(Double.class, map.get("employee.salary").getClass());

✅ 优点:写法简单粗暴,一行搞定
⚠️ 缺点:值类型会被自动推断,比如数字默认是 Double,布尔值是 Boolean,嵌套对象则会变成 LinkedTreeMap

⚠️ 注意:如果有重复 key,会导致解析失败抛出 JsonSyntaxException
❌ 由于 Java 的泛型擦除机制(type erasure),无法指定 key 和 value 的具体类型,灵活性受限。

3. 使用 TypeToken 指定泛型类型

为了解决泛型擦除的问题,Gson 提供了一个重载版本的方法:

public <T> T fromJson(String json, Type typeOfT) throws JsonSyntaxException;

我们可以通过 Gson 提供的 TypeToken 来构建带泛型信息的 Map 类型。TypeToken 可以保留运行时的泛型信息:

String jsonString = "{'Bob' : {'name': 'Bob Willis'},"
  + "'Jenny' : {'name': 'Jenny McCarthy'}, "
  + "'Steve' : {'name': 'Steven Waugh'}}";
Gson gson = new Gson();
Type empMapType = new TypeToken<Map<String, Employee>>() {}.getType();
Map<String, Employee> nameEmployeeMap = gson.fromJson(jsonString, empMapType);
Assert.assertEquals(3, nameEmployeeMap.size());
Assert.assertEquals(Employee.class, nameEmployeeMap.get("Bob").getClass());

✅ 优点:保留泛型信息,支持自定义 key/value 类型
⚠️ 注意:即便使用 Map<String, Object>,基本类型的自动转换行为依然存在(如 int 转 Double)

4. 自定义 JsonDeserializer 实现精细控制

当我们需要对 Map 的构造过程进行更精细控制时,可以实现一个自定义的 JsonDeserializer<Map>

举个例子:假设 JSON 中的 key 是员工姓名,value 是入职日期字符串(格式为 yyyy/MM/dd),而 Gson 默认不支持这种日期格式。

我们可以编写一个自定义的反序列化器:

public class StringDateMapDeserializer implements JsonDeserializer<Map<String, Date>> {

    private SimpleDateFormat format = new SimpleDateFormat("yyyy/MM/dd");

    @Override
    public Map<String, Date> deserialize(JsonElement elem,
          Type type,
          JsonDeserializationContext jsonDeserializationContext) {
        return elem.getAsJsonObject()
          .entrySet()
          .stream()
          .filter(e -> e.getValue().isJsonPrimitive())
          .filter(e -> e.getValue().getAsJsonPrimitive().isString())
          .collect(
            Collectors.toMap(
              Map.Entry::getKey,
              e -> formatDate(e.getValue())));
    }

    private Date formatDate(Object value) {
        try {
            return format.parse(value.getAsString());
        } catch (ParseException ex) {
            throw new JsonParseException(ex);
        }
    }
}

然后注册到 GsonBuilder 中:

String jsonString = "{'Bob': '2017/06/01', 'Jennie':'2015/01/03'}";
Type type = new TypeToken<Map<String, Date>>(){}.getType();
Gson gson = new GsonBuilder()
  .registerTypeAdapter(type, new StringDateMapDeserializer())
  .create();
Map<String, Date> empJoiningDateMap = gson.fromJson(jsonString, type);
Assert.assertEquals(2, empJoiningDateMap.size());
Assert.assertEquals(Date.class, empJoiningDateMap.get("Bob").getClass());

✅ 优点:完全掌控解析逻辑,适合处理异构 value 或特殊格式
⚠️ 注意:需要手动处理异常和类型判断,代码量较大

5. 总结

在这篇文章中,我们介绍了三种将 JSON 字符串转换为 Map 的方式:

方式 使用场景 特点
Map.class 快速原型、无需类型检查 简单直接,但类型受限
TypeToken 需要保留泛型信息 类型安全,推荐日常使用
自定义 JsonDeserializer 复杂格式或精细控制需求 最灵活但也最复杂

示例源码可以在 GitHub 上找到。


原始标题:Convert JSON to a HashMap Using Gson