中文为什么变成 \u4e2d\u6587
结论:\u4e2d\u6587 是合法的 Unicode 转义,不是编码错误。真正的乱码是「测试」这种。分清两者,才知道该改序列化配置还是改文件编码。

结论:看到 \uXXXX 先当作正常转义,用 JSON.parse 试一次就能确认能否还原;要直接显示汉字就关掉序列化端的纯 ASCII 输出(如 ensure_ascii=False),只有传输通道不支持非 ASCII 时才保留转义。
因为它被转义了,不是坏了。
JSON.parse('"\\u4e2d\\u6587"') // "中文"
\u4e2d 就是「中」,\u6587 是「文」。这两串字符是 JSON 字符串里的合法转义序列,解析器读到就会还原。功能上不会出错,只是在日志、抓包、代码评审里读起来很痛苦。
谁在转义
| 工具 | 默认行为 | 开关 |
|---|---|---|
Python json.dumps |
转义(ensure_ascii=True) |
ensure_ascii=False |
JavaScript JSON.stringify |
不转义 | 无 |
| Java Jackson | 不转义 | 可选开启 |
PHP json_encode |
不转义(5.4+) | JSON_UNESCAPED_UNICODE |
Go encoding/json |
不转义 | 无 |
Python 是最高频的来源。它的默认值选择有历史原因:保证输出是纯 ASCII,任何环境都能安全传输。代价就是中文全变转义串。
转义 vs 乱码,先分清
这两种现象经常被混为一谈,但成因完全不同:
转义:\u4e2d\u6587,形如 \uXXXX,每个汉字六个字符。原因:序列化配置。
乱码:测试、涓枃,形如一堆西欧字母或生僻字。原因:字节按 UTF-8 写入,却按 Latin-1 或 GBK 读取。
判断方法很简单:把文本复制到浏览器控制台执行 JSON.parse('"粘贴内容"')。能还原成正常中文就是转义,还原不出来或者直接报错,就是编码问题。
乱码的修法完全不同——要统一读写两端的编码,而不是改转义开关。
想直接看到汉字
Python:
json.dumps(data, ensure_ascii=False)
PHP:
json_encode($data, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES)
改完记得确认下游没问题:有些老系统真的依赖纯 ASCII 传输,尤其是走短信、邮件、旧版网关的场景。
什么时候该保留转义
- 传输通道对非 ASCII 不友好(旧版 HTTP 头、部分短信网关)
- 需要把 JSON 嵌进另一层 JSON 字符串里,转义能少一层引号冲突
- 日志系统按字节截断,多字节字符被切断会产生非法序列
除此之外,可读性优先,关掉转义。
常见问题
为什么 Python 默认把中文转成 \uXXXX?
因为 ensure_ascii=True 是默认值,保证输出是纯 ASCII,任何环境都能安全传输。代价是每个非 ASCII 字符变成六个字符。
要不要关闭 ensure_ascii?
可读性优先时关掉,改完再确认下游:旧版 HTTP 头、部分短信网关、按字节截断的日志系统仍可能依赖纯 ASCII。
传输通道不支持非 ASCII 时是否要保留转义?
要。把 JSON 嵌进另一层 JSON 字符串,或走对非 ASCII 不友好的通道时,转义能减少引号冲突,也能避免多字节字符被截断。
相关工具
- JSON 格式化:格式化后中文会正常显示,同时可以对照查看原始报文
- JSON 与 YAML、XML 的互转边界:跨格式转换时转义规则会变