Why Chinese turns into \u4e2d\u6587
Bottom line: A \u4e2d\u6587 escape is legal Unicode, not an encoding failure. Real mojibake looks different, and telling them apart decides what you actually need to fix.

Bottom line: treat \uXXXX output as legal escaping, confirm it round-trips through JSON.parse, and turn off pure-ASCII serialization such as ensure_ascii=False when you want readable Chinese; keep escaping only for transports that cannot carry non-ASCII.
Because it was escaped, not damaged.
JSON.parse('"\\u4e2d\\u6587"') // "中文"
\u4e2d is 中 and \u6587 is 文. These are legal escape sequences; any parser restores them. Nothing is broken — it is just unreadable in logs, diffs and packet captures.
Who does the escaping
| Tool | Default | Toggle |
|---|---|---|
Python json.dumps |
Escapes (ensure_ascii=True) |
ensure_ascii=False |
JavaScript JSON.stringify |
No escaping | — |
| Java Jackson | No escaping | Optional |
PHP json_encode |
No escaping since 5.4 | — |
Go encoding/json |
No escaping | — |
Python is by far the most common source. The default exists for a reason: pure ASCII output survives any transport. The cost is that every non-ASCII character becomes six characters.
Escaping versus mojibake
Escaping: \u4e2d\u6587, matching \uXXXX. Caused by serializer settings.
Mojibake: 测试, 涓枃. Caused by writing UTF-8 bytes and reading them as Latin-1 or GBK.
Quick test: paste the text into a browser console as JSON.parse('"…"'). If it comes back readable, it was escaping. If it fails or stays garbled, it is an encoding mismatch — and the fix is to align the encodings, not to touch the serializer.
Turning it off
json.dumps(data, ensure_ascii=False)
Then verify downstream: some legacy systems genuinely rely on ASCII-only transport, particularly SMS gateways and older mail relays.
When escaping is the right choice
- Transports that are hostile to non-ASCII (legacy HTTP headers, some SMS gateways)
- JSON nested inside another JSON string, where escaping reduces quote conflicts
- Log pipelines that truncate by bytes and would split a multi-byte character
Otherwise, prefer readability and turn it off.
FAQ
Why does Python escape Chinese by default?
Because ensure_ascii=True is the default, which guarantees pure ASCII output that survives any transport. The cost is that every non-ASCII character becomes six characters.
Should I turn off ensure_ascii?
Turn it off when readability matters. Then verify downstream — legacy HTTP headers, some SMS gateways and log pipelines that truncate by bytes may still rely on ASCII-only content.
What happens if the transport cannot carry non-ASCII?
Keep the escaping. When JSON is embedded inside another JSON string, or travels over a channel that is hostile to non-ASCII, escaping avoids quote conflicts and prevents multi-byte characters from being split.