惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

Cyber Security Advisories - MS-ISAC
Cyber Security Advisories - MS-ISAC
月光博客
月光博客
MyScale Blog
MyScale Blog
OSCHINA 社区最新新闻
OSCHINA 社区最新新闻
爱范儿
爱范儿
P
Proofpoint News Feed
人人都是产品经理
人人都是产品经理
Last Week in AI
Last Week in AI
罗磊的独立博客
G
Google Developers Blog
Y
Y Combinator Blog
博客园 - 【当耐特】
WordPress大学
WordPress大学
大猫的无限游戏
大猫的无限游戏
博客园 - 叶小钗
J
Java Code Geeks
酷 壳 – CoolShell
酷 壳 – CoolShell
V
Visual Studio Blog
美团技术团队
宝玉的分享
宝玉的分享
Jina AI
Jina AI
小众软件
小众软件
T
Tailwind CSS Blog
A
About on SuperTechFans

博客园 - buguge

交易成功还要记 3 笔账?我们直接砍到 1 笔—— 网络红包记账的一次自顶向下业务层重构 分享一次针对hardcode的小重构:不要把 package 名写死在代码里 聚合系统设计:如何为银行代付通道抽象出公共的代付接口能力 如何自定义 MyBatis枚举处理器(EnumTypeHandler):分享从 “同包同名 shadow 覆盖” 到 “官方扩展点” 的重构经历 这儿也改状态,那儿也改状态,连AI都理不清楚了… 不妨试试这个状态机good-practice 业务解耦的经典实践:从订单表解耦开票业务说起(以滴滴网约车开票为例) 从 `int` 到 `Duration`:一个缓存 API 的三次演进教会我的事 最差实践(bad-practice):开发者在方法里直接实例化线程池对象,然后...(应用gg了) 【HttpClient最差实践(bad-practice)】开发者在 http 工具方法中直接实例化 HttpClient,然后…… Crypto、Cipher与Password:Java加密开发的三个核心概念 知识VS技能:如何优雅判空? 20260604SR超时问题排查 推敲见文章:从 `try..catch` 看异常日志打印的正确姿势 #解决问题要彻底# 慢SQL治理完成后,如何防止同类问题“死灰复燃”? 从合同甲方是荒谬的“JD”谈起:软件开发不应遗忘的“常识” 别留小尾巴/尽快剪掉小尾巴:从一次“ABA”字段重命名,谈谈“解决问题要彻底” 常见的OOM错误 ( OutOfMemoryError全类型详解) 开发者暴露了一个无需授权访问的裸接口,我问:如果有人暴力请求怎么办? 【SQL性能优化篇】有了!治理慢SQL“WHERE create_time ORDER BY id”的良药---规避“Using filesort”性能杀手 高效查询商户日终余额:一个SQL的优化实践 Hutool 的 `TimedCache` 到期会自动清理吗? ——————hutool cache的"惰性清理"和"定期清理" fastjson-EnumDeserializer类及源码分析 随笔20260309:我们都是围城里的人 `UnexpectedRollbackException: Transaction rolled back because it has been marked as rollback-only` 异常解析 认识2个单词:goal/target —————— 为什么Maven是 "goal" 而不是 "target"? 聚合系统设计:策略模式(Strategy Pattern)在银行通道对接场景中的应用 这样构建对象,太帅了!—— 阶梯式Builder模式与代码整洁之道 注意!字段数据类型不匹配,这个sql会很慢 还在用ArrayList?用HashSet吧!--性能对比 分页查询还在用create_time去做降序?
Fastjson枚举反序列化:当字符串不是枚举常量名时,会发生什么?
buguge · 2026-03-16 · via 博客园 - buguge

我们知道,对外暴露的 HTTP RestAPI 接口通常使用 JSON 格式传输数据。服务端接收到数据后,会将 JSON 字符串反序列化为对应的请求实体对象。

我司灵工系统使用的是 Fastjson-1.2.83 作为序列化工具。在一次RestAPI开发过程中,我忽然产生一个好奇:当 VO 属性的类型是枚举(enum),而调用方传入的 JSON 字符串不是一个有效的枚举常量值时,Fastjson 会如何处理?

带着这个疑问,我决定一探究竟。

一、测试序幕:一个“意外”的异常

我使用系统内现有的 SourcePlatformEnum 枚举进行测试。

首先,我写了一段代码,将一个枚举实例序列化成 JSON 字符串:

String jsonString = JSON.toJSONString(SourcePlatformEnum.YCX);
System.out.println(jsonString);

运行程序,输出结果为:"YCX"。这符合预期,Fastjson 默认将枚举序列化为其名称字符串。

接着,我尝试将这个字符串反序列化回枚举对象:

String jsonString = "YCX";
System.out.println(JSON.parseObject(jsonString, SourcePlatformEnum.class));

这时,运行程序却意外地抛出了JSONException异常

com.alibaba.fastjson.JSONException: syntax error, pos 1, line 1, column 2YCX

    at com.alibaba.fastjson.parser.DefaultJSONParser.parse(DefaultJSONParser.java:1510)
    at com.alibaba.fastjson.parser.DefaultJSONParser.parse(DefaultJSONParser.java:1390)
    at com.alibaba.fastjson.serializer.StringCodec.deserialze(StringCodec.java:105)
    at com.alibaba.fastjson.serializer.StringCodec.deserialze(StringCodec.java:87)
    at com.alibaba.fastjson.parser.DefaultJSONParser.parseObject(DefaultJSONParser.java:705)
    at com.alibaba.fastjson.JSON.parseObject(JSON.java:394)
    at com.alibaba.fastjson.JSON.parseObject(JSON.java:298)
    at com.alibaba.fastjson.JSON.parseObject(JSON.java:588)
    at com.levy.openapi.TestMain.test(TestMain.java:18)
    ...

问题出在哪里?

原来,错误在于 jsonString 的赋值。"YCX" 只是一个普通的 Java 字符串,并非合法的 JSON 字符串。在 JSON 格式中,字符串值必须由双引号包裹。

正确的写法应该是:String jsonString = "\"YCX\"";

这个小小的插曲给了我一个重要的提醒:在进行任何反序列化测试前,首先要确保输入的 JSON 格式是正确的

二、正式测试:四种情况下的行为

修正了格式错误后,我使用fastjson-1.2.83系统地测试了四种常见情况,结果如下:

用例 输入的 JSON 字符串 Fastjson 反序列化后的 SourcePlatformEnum
Case 1 null null
Case 2 "" (空字符串) null
Case 3 "YCX" (有效的枚举常量名) SourcePlatformEnum.YCX
Case 4 "INVALID_VALUE" (无效的枚举常量名) null
Case 5 "null" (无效的枚举常量名) null
Case 6 "ycX" (大小写不一致) SourcePlatformEnum.YCX

测试结果非常明确:

  1. 当输入为 null 或空字符串时,反序列化结果为 null
  2. 当输入是有效的枚举常量名(大小写不敏感)时,能正确反序列化为对应的枚举实例。
  3. 当输入是无效的枚举常量名时,反序列化结果同样为 null,而不会抛出异常。这是Fastjson的默认行为。

一句话总结为:Fastjson 在枚举反序列化时,对非法值采取了 “静默返回 null” 的策略,而非抛出异常。

注:fastjson-1.2.83枚举反序列化器源码是com.alibaba.fastjson.parser.deserializer.EnumDeserializer。

三、如何权衡:API设计中能否使用枚举类型?

那么,在实际的 API 接口设计中,对于那些有对应枚举类的请求参数,能否使用枚举作为其数据类型呢?

我认为,关键在于如何理解并处理上面的 Case 4

  • 如果某个参数是极少变化的、明确的、必传的,那么将其定义为枚举类型是非常合适的。这本身就是一种强类型的契约,能清晰地告知调用方允许的取值范围。即使对方传了非法值,在服务端接收到的是一个 null,我们可以在业务校验中轻松捕获并返回明确的参数错误提示。

  • 而对于那些非必填的、不定期会有变化的参数,则可仍然使用基本Java类型。当然,如果不介意非法值被静默转换为 null,定义为枚举类型在代码可读性上也有其价值。

以我司商户API接口为例:我们采用的是门面模式,即所有 API 共用一个 URL,通过必传的 funCode 参数来区分具体业务。这个 funCode 我们就将其定义为 FunCodeEnum 枚举类型。而如查询交易接口中的交易类型,这个可选参数可以为基本Java类型,这样当出现问题时,通过原始入参值更容易排查。