前后端 JSON 交互的 10 个常见坑与解决方案
前后端通过 JSON 交互时,经常遇到各种问题。本文总结 10 个最常见的坑和解决方案。
坑 1:数字精度丢失
问题: JavaScript 的 Number 类型最大安全整数是 2^53 - 1,超过会丢失精度。
// ❌ 精度丢失
JSON.parse('{"id": 9007199254740993}')
// { id: 9007199254740992 } ← 最后一位变了!
解决方案:
// 方案1:后端返回字符串
{ "id": "9007199254740993" }
// 方案2:使用 BigInt
BigInt("9007199254740993")
// 方案3:使用 json-bigint 库
const JSONbig = require('json-bigint');
JSONbig.parse('{"id": 9007199254740993}');
坑 2:日期格式不一致
问题: JSON 没有日期类型,不同系统格式不同。
// Java 返回的日期
{ "createTime": "2024-01-15T10:30:00.000+08:00" }
// 前端期望的格式
{ "createTime": "2024-01-15 10:30:00" }
// 有时甚至是时间戳
{ "createTime": 1705286400000 }
解决方案:
// 后端统一格式
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
private Date createTime;
// 前端统一处理
function formatDate(dateStr) {
return new Date(dateStr).toLocaleString('zh-CN');
}
坑 3:null vs undefined
问题: Java 中 null 和 JavaScript 中 undefined 行为不同。
// JSON 中只有 null,没有 undefined
JSON.stringify(undefined) // undefined(不是字符串)
JSON.stringify(null) // "null"
// 访问不存在的属性
const obj = { name: "张三" };
obj.age // undefined
obj.age ?? 0 // 0(空值合并)
解决方案:
// 使用空值合并运算符
const age = data.age ?? 0;
// 使用可选链
const city = data.address?.city ?? '未知';
坑 4:中文编码问题
问题: 中文字符在传输过程中可能乱码。
解决方案:
// 后端设置编码
produces = "application/json; charset=UTF-8"
// 前端确保正确解码
const response = await fetch(url);
const data = await response.json(); // 自动处理 UTF-8
坑 5:大数字作为 ID
问题: 数据库自增 ID 超过 JavaScript 安全整数范围。
解决方案:
// 后端返回字符串 ID
@JsonSerialize(using = ToStringSerializer.class)
private Long id;
坑 6:数组 vs 对象
问题: 空数组和空对象序列化结果不同。
JSON.stringify([]) // "[]"
JSON.stringify({}) // "{}"
// Java 中 List 和 Map 序列化结果也不同
坑 7:字段名大小写
问题: Java 驼峰命名 vs 数据库下划线命名。
// 使用注解统一
@JsonProperty("user_name")
private String userName;
坑 8:循环引用
问题: 对象之间相互引用导致序列化失败。
// ❌ 循环引用
class User {
Order order;
}
class Order {
User user;
}
// 解决:使用 @JsonIgnore
@JsonIgnore
private User user;
坑 9:枚举类型处理
问题: 前后端枚举值不一致。
// 后端返回枚举值
@JsonValue
private int code;
// 前端使用常量
const STATUS = {
PENDING: 0,
APPROVED: 1,
REJECTED: 2
};
坑 10:空字符串 vs null
问题: 空字符串和 null 的处理逻辑不同。
// 区分空字符串和 null
if (data.name === '') {
// 用户清空了输入
} else if (data.name === null) {
// 字段未填写
} else {
// 有值
}
最佳实践总结
- 统一日期格式:使用 ISO 8601 或时间戳
- ID 用字符串:避免大数字精度问题
- 字段名统一:使用驼峰或下划线,不要混用
- 空值处理:明确 null 和空字符串的区别
- 类型一致:前后端数据类型要对应
- 文档化:使用 Swagger/OpenAPI 文档化 API
总结
前后端 JSON 交互的问题大多源于数据类型和格式不一致。通过制定统一的规范、使用注解和工具,可以避免大部分问题。