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 上找到。