Dlaczego chiński zamienia się w \u4e2d\u6587
Podsumowanie: Sekwencja ucieczki \u4e2d\u6587 to legalny Unicode, a nie awaria kodowania. Prawdziwy mojibake wygląda inaczej, a rozróżnienie ich decyduje, co naprawdę trzeba naprawić.

Najważniejsze: traktuj wyjście \uXXXX jako legalną ucieczkę, potwierdź, że przechodzi w obie strony przez JSON.parse, i wyłącz serializację czysto ASCII, taką jak ensure_ascii=False, gdy chcesz czytelnego chińskiego; zachowaj ucieczkę tylko dla transportów, które nie potrafią nieść nie-ASCII.
Bo został escape'owany, a nie uszkodzony.
JSON.parse('"\\u4e2d\\u6587"') // "中文"
\u4e2d to 中, a \u6587 to 文. To legalne sekwencje ucieczki; każdy parser je przywraca. Nic nie jest zepsute — jest tylko nieczytelne w logach, diffach i przechwyconym ruchu.
Kto wykonuje ucieczkę
| Narzędzie | Domyślnie | Przełącznik |
|---|---|---|
Python json.dumps |
Escape'uje (ensure_ascii=True) |
ensure_ascii=False |
JavaScript JSON.stringify |
Nie escape'uje | — |
| Java Jackson | Nie escape'uje | Opcjonalne |
PHP json_encode |
Nie escape'uje od 5.4 | — |
Go encoding/json |
Nie escape'uje | — |
Python jest zdecydowanie najczęstszym źródłem. Domyślność istnieje nie bez powodu: wyjście czysto ASCII przetrwa każdy transport. Kosztem jest to, że każdy znak nie-ASCII staje się sześcioma znakami.
Ucieczka a mojibake
Ucieczka: \u4e2d\u6587, pasujące do \uXXXX. Spowodowane ustawieniami serializatora.
Mojibake: 测试, 涓枃. Spowodowane zapisaniem bajtów UTF-8 i odczytaniem ich jako Latin-1 lub GBK.
Szybki test: wklej tekst do konsoli przeglądarki jako JSON.parse('"…"'). Jeśli wraca czytelnie, to była ucieczka. Jeśli zawodzi lub pozostaje zniekształcone, to niezgodność kodowań — a poprawką jest wyrównanie kodowań, a nie dotykanie serializatora.
Wyłączanie jej
json.dumps(data, ensure_ascii=False)
Potem zweryfikuj downstream: niektóre odziedziczone systemy naprawdę polegają na transporcie wyłącznie ASCII, zwłaszcza bramki SMS i starsze przekaźniki mailowe.
Kiedy ucieczka jest właściwym wyborem
- Transporty wrogie wobec nie-ASCII (odziedziczone nagłówki HTTP, część bramek SMS)
- JSON osadzony w innym łańcuchu JSON, gdzie ucieczka redukuje konflikty cudzysłowów
- Potoki logów obcinające po bajtach, które podzieliłyby znak wielobajtowy
Poza tym preferuj czytelność i wyłącz ją.
FAQ
Dlaczego Python domyślnie escape'uje chiński?
Bo ensure_ascii=True jest domyślne, co gwarantuje wyjście czysto ASCII, które przetrwa każdy transport. Kosztem jest to, że każdy znak nie-ASCII staje się sześcioma znakami.
Czy wyłączać ensure_ascii?
Wyłącz, gdy liczy się czytelność. Potem zweryfikuj downstream — odziedziczone nagłówki HTTP, część bramek SMS i potoki logów obcinające po bajtach mogą nadal polegać na treści wyłącznie ASCII.
Co jeśli transport nie potrafi nieść nie-ASCII?
Zachowaj ucieczkę. Gdy JSON jest osadzony w innym łańcuchu JSON albo podróżuje kanałem wrogim wobec nie-ASCII, ucieczka unika konfliktów cudzysłowów i zapobiega podziałowi znaków wielobajtowych.