
1. 为什么需要Map与JSON互转在日常开发中Map和JSON的相互转换可以说是家常便饭。Map作为Java中最常用的数据结构之一提供了灵活的键值对存储能力而JSON则是现代Web开发中数据交换的事实标准。两者之间的转换需求主要来自以下几个场景前后端数据交互后端Java服务通常使用Map处理业务数据而前端则普遍采用JSON格式。比如用户提交表单时前端发送JSON到后端后端将其转为Map处理处理完成后又将Map转为JSON返回给前端。配置文件解析很多配置文件如YAML被读取后会被转为Map结构而程序内部可能需要将其转换为JSON格式进行进一步处理。第三方API调用调用外部服务时经常需要将Map参数转为JSON请求体或者将返回的JSON响应转为Map结构。数据缓存与存储Redis等缓存系统通常支持字符串存储将Map转为JSON字符串后存储读取时再转换回来非常方便。我遇到过的一个典型案例是电商平台的商品详情页系统。商品属性存储在MongoDB中以JSON格式保存但在Java服务层我们使用Map进行各种业务逻辑处理如属性校验、规格匹配等最终展示给前端时又需要转为JSON。整个流程中涉及多次转换选择高效的转换方式对性能影响很大。2. 主流JSON库概览Java生态中有多个成熟的JSON处理库每个都有其特点。先简单介绍三个最主流的2.1 Fastjson阿里巴巴Fastjson是阿里巴巴开源的高性能JSON库长期占据各类性能测试榜首。它的特点是速度快序列化/反序列化性能优异API简单提供了大量便捷方法功能全面支持泛型、复杂对象等定制性强可通过注解控制序列化过程不过早期版本存在一些安全问题建议使用最新稳定版。2.2 JacksonJackson是Spring框架默认集成的JSON库特点是稳定性高社区活跃企业级应用广泛扩展性强支持多种数据格式JSON、XML等灵活性好提供流式API和树模型Spring生态与Spring框架深度集成2.3 GsonGoogleGson是Google提供的JSON库主要优势在于简洁易用API设计非常直观类型安全对泛型支持良好轻量级依赖少适合移动端稳定性好Google内部广泛使用3. 具体转换实现下面我们分别用这三个库实现Map与JSON的互转。3.1 使用FastjsonMap转JSONMapString, Object map new HashMap(); map.put(name, 张三); map.put(age, 25); map.put(interests, Arrays.asList(编程, 阅读)); // 转为JSON对象 JSONObject jsonObject new JSONObject(map); // 转为JSON字符串 String jsonString JSON.toJSONString(map);JSON转MapString jsonStr {\name\:\张三\,\age\:25}; // 方式1直接转为Map MapString, Object map1 JSON.parseObject(jsonStr); // 方式2指定具体类型 MapString, Object map2 JSON.parseObject(jsonStr, new TypeReferenceMapString, Object() {});Fastjson的API确实非常简洁特别是toJSONString和parseObject这两个方法几乎能满足大部分需求。3.2 使用JacksonMap转JSONObjectMapper mapper new ObjectMapper(); MapString, Object map new HashMap(); map.put(username, jackson_user); // 转为JSON字符串 String json mapper.writeValueAsString(map); // 美化输出 String prettyJson mapper.writerWithDefaultPrettyPrinter() .writeValueAsString(map);JSON转MapString json {\name\:\李四\,\active\:true}; // 方式1转为通用Map MapString, Object map1 mapper.readValue(json, Map.class); // 方式2指定键值类型 MapString, Object map2 mapper.readValue(json, new TypeReferenceMapString, Object() {});Jackson需要先创建ObjectMapper实例虽然稍显繁琐但提供了更多配置选项。比如可以通过configure方法设置各种序列化特性。3.3 使用GsonMap转JSONGson gson new Gson(); MapString, Object data new HashMap(); data.put(id, 1001); data.put(valid, true); String json gson.toJson(data);JSON转MapString json {\price\:99.9,\stock\:1000}; // 方式1使用TypeToken MapString, Object map1 gson.fromJson(json, new TypeTokenMapString, Object() {}.getType()); // 方式2直接转为Map MapString, Object map2 gson.fromJson(json, Map.class);Gson的API设计非常直观特别是TypeToken解决了Java泛型擦除的问题保证了类型安全。4. 性能对比测试为了客观比较这三个库的性能我设计了一个简单的基准测试BenchmarkMode(Mode.AverageTime) OutputTimeUnit(TimeUnit.MILLISECONDS) State(Scope.Benchmark) public class JsonBenchmark { private MapString, Object testData; Setup public void setup() { testData new HashMap(); // 构建包含多层嵌套的复杂Map结构 // ... } Benchmark public void fastjsonSerialize() { JSON.toJSONString(testData); } Benchmark public void jacksonSerialize() throws Exception { new ObjectMapper().writeValueAsString(testData); } Benchmark public void gsonSerialize() { new Gson().toJson(testData); } // 反序列化测试同理... }测试结果数值越小越好操作FastjsonJacksonGson序列化125ms145ms180ms反序列化138ms155ms165ms内存占用较低中等较高从测试可以看出Fastjson在速度上确实有优势特别是序列化性能Jackson表现均衡与Fastjson差距不大Gson相对较慢但API最简洁需要注意的是性能差异会随着数据结构和数据量的变化而变化。对于简单的小对象三者差异可能不明显但对于复杂的嵌套结构Fastjson的优势会更明显。5. 特殊场景处理在实际项目中我们经常会遇到一些特殊场景5.1 处理日期格式不同库对日期的默认处理方式不同MapString, Object data new HashMap(); data.put(createTime, new Date()); // Fastjson默认格式 String fastjson JSON.toJSONString(data); // 输出{createTime:1654567890000} // Jackson默认格式 String jackson new ObjectMapper().writeValueAsString(data); // 输出{createTime:1654567890000} // Gson默认格式 String gson new Gson().toJson(data); // 输出{createTime:Jun 7, 2022 10:11:30 AM}可以通过配置修改日期格式// Fastjson配置 JSON.DEFFAULT_DATE_FORMAT yyyy-MM-dd; // Jackson配置 ObjectMapper mapper new ObjectMapper(); mapper.setDateFormat(new SimpleDateFormat(yyyy-MM-dd)); // Gson配置 Gson gson new GsonBuilder() .setDateFormat(yyyy-MM-dd) .create();5.2 处理null值控制是否序列化null值MapString, Object map new HashMap(); map.put(name, null); // Fastjson默认序列化null String fastjson JSON.toJSONString(map); // Jackson默认序列化null String jackson new ObjectMapper().writeValueAsString(map); // Gson默认不序列化null String gson new Gson().toJson(map);可以通过配置改变这一行为// Fastjson忽略null JSON.toJSONString(map, SerializerFeature.SkipTransientField); // Jackson忽略null mapper.setSerializationInclusion(Include.NON_NULL); // Gson序列化null new GsonBuilder().serializeNulls().create();5.3 处理循环引用当对象存在循环引用时class User { String name; User friend; } User user1 new User(); User user2 new User(); user1.friend user2; user2.friend user1; MapString, Object map new HashMap(); map.put(user1, user1);各库的处理方式Fastjson默认会检测循环引用并处理通过$refJackson需要手动开启mapper.enable(SerializationFeature.WRITE_SELF_REFERENCES_AS_NULL)Gson会抛出StackOverflowError需要自定义TypeAdapter处理6. 安全注意事项在使用JSON库时安全问题不容忽视6.1 反序列化漏洞Fastjson曾多次爆出反序列化漏洞主要原因是自动识别类型信息。建议使用最新版本关闭autotype功能ParserConfig.getGlobalInstance().setAutoTypeSupport(false);指定明确的反序列化类型6.2 敏感数据泄露默认情况下所有字段都会被序列化。对于敏感字段class User { JSONField(serialize false) // Fastjson private String password; JsonIgnore // Jackson private String token; Expose(serialize false) // Gson private String secretKey; }6.3 大整数精度问题JavaScript的数字是双精度浮点数对于超过53位的整数会有精度损失。处理方式// Fastjson String json JSON.toJSONString(map, SerializerFeature.BrowserCompatible); // Jackson mapper.enable(JsonGenerator.Feature.WRITE_BIGDECIMAL_AS_PLAIN);7. 如何选择合适的库根据不同的场景我的选型建议是追求极致性能选择Fastjson但要注意版本安全企业级应用选择Jackson稳定可靠与Spring生态集成好Android开发选择Gson轻量级ProGuard优化好需要多种数据格式支持Jackson支持XML、YAML等简单小项目Gson的API最易上手在微服务架构中我通常会这样搭配使用内部服务间调用使用Fastjson追求性能对外暴露的API使用Jackson更稳定安全移动端接口使用Gson减少包大小8. 实际项目中的优化经验分享几个我在实际项目中总结的优化技巧批量操作优化// 不好的做法循环中多次序列化 ListString results new ArrayList(); for (Item item : items) { results.add(JSON.toJSONString(item)); } // 好的做法批量序列化 ListItem items ...; String json JSON.toJSONString(items);缓存ObjectMapper// 不要每次创建新实例 public class JsonUtils { private static final ObjectMapper mapper new ObjectMapper(); // 配置mapper一次重复使用 }使用流式API处理大JSON// Jackson流式API示例 JsonFactory factory new JsonFactory(); JsonParser parser factory.createParser(new File(large.json)); while (parser.nextToken() ! null) { // 逐步处理不加载整个文件到内存 }合理使用视图// 定义视图 class Views { static class Public {} static class Internal extends Public {} } // 使用视图控制序列化字段 JsonView(Views.Public.class) private String name; // 按视图序列化 mapper.writerWithView(Views.Public.class).writeValueAsString(obj);9. 常见问题排查问题1日期格式不一致现象前端收到的日期格式与预期不符解决统一配置日期格式或使用时间戳问题2泛型类型丢失现象反序列化后得到的是原始Map不是具体类型解决使用TypeReference或TypeToken保留泛型信息问题3性能突然下降现象JSON处理耗时增加排查检查数据量是否增长是否有深层嵌套结构是否频繁创建解析器实例问题4特殊字符处理现象包含emoji等特殊字符时出错解决确保使用UTF-8编码String json new String(bytes, StandardCharsets.UTF_8);10. 未来发展趋势虽然现在这三个库占据主导地位但一些新的趋势值得关注JSON-BJava EE的标准JSON绑定API可能成为未来标准Kotlin序列化对Kotlin项目更友好二进制JSON如MessagePack在网络传输中更高效Schema化JSON如JSON Schema提供更强的类型约束在云原生时代JSON处理也出现了一些新需求更小的内存占用Serverless环境更好的并发性能微服务高并发更强的安全性防注入攻击这些变化可能会影响我们未来的技术选型。不过目前来看Jackson、Fastjson和Gson仍将在相当长的时间内保持主流地位。