找回密码
 立即注册
首页 业界区 安全 Fastjson枚举反序列化:当字符串不是枚举常量名时,会发 ...

Fastjson枚举反序列化:当字符串不是枚举常量名时,会发生什么?

鲫疹 3 天前
我们知道,对外暴露的 HTTP RestAPI 接口通常使用 JSON 格式传输数据。服务端接收到数据后,会将 JSON 字符串反序列化为对应的请求实体对象。
我司灵工系统使用的是 Fastjson-1.2.83 作为序列化工具。在一次RestAPI开发过程中,我忽然产生一个好奇:当 VO 属性的类型是枚举(enum),而调用方传入的 JSON 字符串不是一个有效的枚举常量值时,Fastjson 会如何处理?
带着这个疑问,我决定一探究竟。
一、测试序幕:一个“意外”的异常

我使用系统内现有的 SourcePlatformEnum 枚举进行测试。
首先,我写了一段代码,将一个枚举实例序列化成 JSON 字符串:
  1. String jsonString = JSON.toJSONString(SourcePlatformEnum.YCX);
  2. System.out.println(jsonString);
复制代码
运行程序,输出结果为:"YCX"。这符合预期,Fastjson 默认将枚举序列化为其名称字符串。
接着,我尝试将这个字符串反序列化回枚举对象:
  1. String jsonString = "YCX";
  2. System.out.println(JSON.parseObject(jsonString, SourcePlatformEnum.class));
复制代码
这时,运行程序却意外地抛出了JSONException异常
  1. com.alibaba.fastjson.JSONException: syntax error, pos 1, line 1, column 2YCX
  2.     at com.alibaba.fastjson.parser.DefaultJSONParser.parse(DefaultJSONParser.java:1510)
  3.     at com.alibaba.fastjson.parser.DefaultJSONParser.parse(DefaultJSONParser.java:1390)
  4.     at com.alibaba.fastjson.serializer.StringCodec.deserialze(StringCodec.java:105)
  5.     at com.alibaba.fastjson.serializer.StringCodec.deserialze(StringCodec.java:87)
  6.     at com.alibaba.fastjson.parser.DefaultJSONParser.parseObject(DefaultJSONParser.java:705)
  7.     at com.alibaba.fastjson.JSON.parseObject(JSON.java:394)
  8.     at com.alibaba.fastjson.JSON.parseObject(JSON.java:298)
  9.     at com.alibaba.fastjson.JSON.parseObject(JSON.java:588)
  10.     at com.levy.openapi.TestMain.test(TestMain.java:18)
  11.     ...
复制代码
问题出在哪里?
原来,错误在于 jsonString 的赋值。"YCX" 只是一个普通的 Java 字符串,并非合法的 JSON 字符串。在 JSON 格式中,字符串值必须由双引号包裹。
正确的写法应该是:String jsonString = "\"YCX\"";
这个小小的插曲给了我一个重要的提醒:在进行任何反序列化测试前,首先要确保输入的 JSON 格式是正确的
二、正式测试:四种情况下的行为

修正了格式错误后,我使用fastjson-1.2.83系统地测试了四种常见情况,结果如下:
用例输入的 JSON 字符串Fastjson 反序列化后的 SourcePlatformEnum 值Case 1nullnullCase 2"" (空字符串)nullCase 3"YCX" (有效的枚举常量名)SourcePlatformEnum.YCXCase 4"INVALID_VALUE" (无效的枚举常量名)nullCase 5"null" (无效的枚举常量名)nullCase 6"ycX" (大小写不一致)SourcePlatformEnum.YCX测试结果非常明确:

  • 当输入为 null 或空字符串时,反序列化结果为 null。
  • 当输入是有效的枚举常量名(大小写不敏感)时,能正确反序列化为对应的枚举实例。
  • 当输入是无效的枚举常量名时,反序列化结果同样为 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类型,这样当出现问题时,通过原始入参值更容易排查。

来源:程序园用户自行投稿发布,如果侵权,请联系站长删除
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!

相关推荐

您需要登录后才可以回帖 登录 | 立即注册