问题描述
当字段类型为 byte[],并通过 JSONReader.Feature.Base64StringAsByteArray
开启 Base64 字符串反序列化支持后,如果 JSON 中该 Base64 字符串值的结束引号
后面紧跟任意空白字符(空格 / 换行 / Tab,即被 PrettyFormat 美化过、或手写带
缩进的 JSON),解析器会计算错误的游标位置,抛出
JSONException: illegal fieldName。
只要该字符串值后面紧跟 } 或 ,(不含任何空白)就能正常解析;一旦中间
夹了空白字符,无论是全局配置还是单次调用配置该 Feature,也无论目标类是
字段还是 getter/setter 属性,都会触发同样的报错。
环境信息
- OS: Windows 11 / 也在其他环境复现,与 OS 无关
- JDK: 21(在 JDK 8/11 下逻辑路径相同,理论上同样受影响,未逐一验证)
- 版本信息:Fastjson2 2.0.58 ~ 2.0.62(已验证),怀疑 2.0.64 仍未修复
(最新发行说明未提及相关修复)
重现步骤
import com.alibaba.fastjson2.JSON;
import com.alibaba.fastjson2.JSONReader;
public class Base64WhitespaceBugDemo {
public static class TestData {
private byte[] data;
public byte[] getData() { return data; }
public void setData(byte[] data) { this.data = data; }
}
public static void main(String[] args) {
JSON.config(JSONReader.Feature.Base64StringAsByteArray);
// 1. 紧凑 JSON:字符串值后紧跟 } ,正常
String compactJson = "{\"data\":\"aGVsbG8gd29ybGQ=\"}";
TestData ok = JSON.parseObject(compactJson, TestData.class);
System.out.println("compact OK, len=" + ok.getData().length); // 输出: 11
// 2. 美化/缩进 JSON:字符串值后紧跟换行再 } ,抛出异常
String prettyJson = "{\n\t\"data\":\"aGVsbG8gd29ybGQ=\"\n}";
TestData fail = JSON.parseObject(prettyJson, TestData.class); // 抛出异常
}
}
实际结果
第 2 步抛出异常:
com.alibaba.fastjson2.JSONException: illegal fieldName input10, offset 29, character
, line 3, column 1, fastjson-version 2.0.61
{
"data":"aGVsbG8gd29ybGQ="
}
at com.alibaba.fastjson2.JSONReader.readFieldNameHashCodeError(JSONReader.java:6885)
at com.alibaba.fastjson2.JSONReaderASCII.readFieldNameHashCodeE(JSONReaderASCII.java:266)
at com.alibaba.fastjson2.reader.ORG_1_1_TestData.readObject(Unknown Source)
at com.alibaba.fastjson2.JSON.parseObject(JSON.java:992)
期待的正确结果
第 2 步应该和第 1 步一样正常解析出 len=11,Base64 字符串值后面是否跟有
格式化空白字符不应该影响解析结果(JSON 规范里结构性 token 之间允许任意空白)。
补充:版本二分定位与根因分析
用同一段复现代码在本地测试了 com.alibaba.fastjson2:fastjson2 从
2.0.11 到 2.0.62 的多个版本,结果:
| 版本区间 |
结果 |
| 2.0.14 ~ 2.0.57 |
正常 |
| 2.0.58 |
开始出现该异常 |
| 2.0.59 ~ 2.0.62 |
持续存在 |
对照 2.0.58 发行说明
第 10 条「支持更多场景base64字符串反序列化」对应的
#3497,推测是该版本
为支持 data:image/jpeg;base64, 前缀而给 ObjectReaderImplInt8ValueArray
新增/改造了 jsonReader.readBase64() 快速路径,这条快速路径在字符串值
结束后跳过尾部空白的游标推进逻辑存在问题。
补充:进一步缩小触发条件
{"data":"aGVsbG8gd29ybGQ="} -> OK
{"data":"aGVsbG8gd29ybGQ="\n} -> FAIL(仅尾部换行)
{"data":"aGVsbG8gd29ybGQ=" } -> FAIL(仅尾部空格)
{\n\t"data":"aGVsbG8gd29ybGQ="} -> OK(仅字段名前的空白不受影响)
即:字段名前的空白不影响解析,只有 Base64 字符串值结束引号之后的
空白会触发该 bug;触发条件与是否为全局 JSON.config(...) 还是单次调用
JSON.parseObject(json, Class, Feature...) 无关,与目标类是裸字段还是
getter/setter 属性也无关。
影响范围
任何在业务代码里为了兼容 fastjson 1.x 的 Base64 序列化行为而开启
Base64StringAsByteArray 的项目,只要反序列化的 JSON 是经过美化/缩进
(比如用 JSONWriter.Feature.PrettyFormat 输出后又被重新解析,或手写
测试 fixture 保留了缩进换行),都会踩到这个问题。
问题描述
当字段类型为
byte[],并通过JSONReader.Feature.Base64StringAsByteArray开启 Base64 字符串反序列化支持后,如果 JSON 中该 Base64 字符串值的结束引号
后面紧跟任意空白字符(空格 / 换行 / Tab,即被 PrettyFormat 美化过、或手写带
缩进的 JSON),解析器会计算错误的游标位置,抛出
JSONException: illegal fieldName。只要该字符串值后面紧跟
}或,(不含任何空白)就能正常解析;一旦中间夹了空白字符,无论是全局配置还是单次调用配置该 Feature,也无论目标类是
字段还是 getter/setter 属性,都会触发同样的报错。
环境信息
(最新发行说明未提及相关修复)
重现步骤
实际结果
第 2 步抛出异常:
期待的正确结果
第 2 步应该和第 1 步一样正常解析出
len=11,Base64 字符串值后面是否跟有格式化空白字符不应该影响解析结果(JSON 规范里结构性 token 之间允许任意空白)。
补充:版本二分定位与根因分析
用同一段复现代码在本地测试了
com.alibaba.fastjson2:fastjson2从2.0.11 到 2.0.62 的多个版本,结果:
对照 2.0.58 发行说明
第 10 条「支持更多场景base64字符串反序列化」对应的
#3497,推测是该版本
为支持
data:image/jpeg;base64,前缀而给ObjectReaderImplInt8ValueArray新增/改造了
jsonReader.readBase64()快速路径,这条快速路径在字符串值结束后跳过尾部空白的游标推进逻辑存在问题。
补充:进一步缩小触发条件
即:字段名前的空白不影响解析,只有 Base64 字符串值结束引号之后的
空白会触发该 bug;触发条件与是否为全局
JSON.config(...)还是单次调用JSON.parseObject(json, Class, Feature...)无关,与目标类是裸字段还是getter/setter 属性也无关。
影响范围
任何在业务代码里为了兼容 fastjson 1.x 的 Base64 序列化行为而开启
Base64StringAsByteArray的项目,只要反序列化的 JSON 是经过美化/缩进(比如用
JSONWriter.Feature.PrettyFormat输出后又被重新解析,或手写测试 fixture 保留了缩进换行),都会踩到这个问题。