Почему китайский превращается в \u4e2d\u6587
Главное: Escape-последовательность \u4e2d\u6587 — это легитимный Unicode, а не сбой кодировки. Настоящее искажение выглядит иначе, и различение решает, что именно нужно исправлять.

Главное: считайте вывод \uXXXX легитимным экранированием, подтвердите, что он проходит туда-обратно через JSON.parse, и отключите чисто-ASCII сериализацию вроде ensure_ascii=False, когда нужен читаемый китайский; сохраняйте экранирование только для транспортов, не способных нести не-ASCII.
Потому что он был экранирован, а не повреждён.
JSON.parse('"\\u4e2d\\u6587"') // "中文"
\u4e2d — это 中, а \u6587 — это 文. Это легитимные escape-последовательности; любой парсер их восстанавливает. Ничего не сломано — просто нечитаемо в логах, различиях и захватах пакетов.
Кто делает экранирование
| Инструмент | По умолчанию | Переключатель |
|---|---|---|
Python json.dumps |
Экранирует (ensure_ascii=True) |
ensure_ascii=False |
JavaScript JSON.stringify |
Не экранирует | — |
| Java Jackson | Не экранирует | Опционально |
PHP json_encode |
Не экранирует с 5.4 | — |
Go encoding/json |
Не экранирует | — |
Python — безусловно самый частый источник. Значение по умолчанию существует по причине: чисто-ASCII вывод выживает в любом транспорте. Цена — каждый не-ASCII символ становится шестью символами.
Экранирование против искажения
Экранирование: \u4e2d\u6587, соответствует \uXXXX. Вызвано настройками сериализатора.
Искажение: 测试, 涓枃. Вызвано записью UTF-8-байтов и чтением их как Latin-1 или GBK.
Быстрый тест: вставьте текст в консоль браузера как JSON.parse('"…"'). Если он возвращается читаемым, это было экранирование. Если падает или остаётся искажённым, это несовпадение кодировок — и исправление в том, чтобы выровнять кодировки, а не трогать сериализатор.
Отключение
json.dumps(data, ensure_ascii=False)
Затем проверьте нижестоящие системы: некоторые устаревшие системы действительно рассчитывают на чисто-ASCII транспорт, особенно SMS-шлюзы и старые почтовые релеи.
Когда экранирование — правильный выбор
- Транспорты, враждебные к не-ASCII (устаревшие HTTP-заголовки, некоторые SMS-шлюзы)
- JSON, вложенный внутрь другой строки JSON, где экранирование уменьшает конфликты кавычек
- Конвейеры логов, которые обрезают по байтам и разорвали бы многобайтовый символ
В остальном предпочитайте читаемость и отключите его.
Вопросы и ответы
Почему Python по умолчанию экранирует китайский?
Потому что ensure_ascii=True — значение по умолчанию, гарантирующее чисто-ASCII вывод, который выживает в любом транспорте. Цена — каждый не-ASCII символ становится шестью символами.
Стоит ли отключать ensure_ascii?
Отключайте, когда важна читаемость. Затем проверьте нижестоящие системы — устаревшие HTTP-заголовки, некоторые SMS-шлюзы и конвейеры логов, обрезающие по байтам, могут всё ещё рассчитывать на чисто-ASCII содержимое.
Что происходит, если транспорт не может нести не-ASCII?
Сохраняйте экранирование. Когда JSON вложен внутрь другой строки JSON или идёт через канал, враждебный к не-ASCII, экранирование избегает конфликтов кавычек и предотвращает разрыв многобайтовых символов.