Python 2では、HTTPレスポンスとしてJSONデータを送信する際、中国語などの非ASCII文字が`\uXXXX`という形式で表示されることがあります。これは、JSONライブラリのデフォルトの動作によるものです。
Python 2の文字列処理は、バイト列(bytes)とUnicode文字列の間で変換を行います。`decode`メソッドはバイト列を指定されたエンコーディングでUnicodeに変換し、`encode`メソッドはUnicodeをバイト列に変換します。データはバイト列としてやり取りされますが、正しいエンコーディングでデコードしなければ、意図した文字列として表示できません。
この問題の原因は、JSONライブラリの`ensure_ascii`パラメータにあります。`json.dumps()`関数は、このパラメータのデフォルト値が`True`であるため、非ASCII文字をすべてUnicodeエスケープシーケンス(`\uXXXX`)に変換してしまいます。
def JSONをダンプする(data, file_obj, skipkeys=False, ensure_ascii=True, check_circular=True,
allow_nan=True, cls=None, indent=None, separators=None,
encoding='utf-8', default=None, sort_keys=False, **kw):
"""オブジェクトをJSON形式のストリームとしてファイルオブジェクトにシリアライズします。
`ensure_ascii`がTrue(デフォルト)の場合、出力中のすべての非ASCII文字は`\uXXXX`シーケンスでエスケープされ、結果はASCII文字のみで構成される`str`インスタンスになります。
`ensure_ascii`がFalseの場合、`fp.write()`が明示的にUnicodeを理解しない限り、エラーが発生する可能性があります。
"""
...
人気のあるWebフレームワークであるTornadoも、このデフォルト動作に依存しています。Tornadoの`RequestHandler`クラスの`write`メソッドは、辞書型のデータを受け取ると、内部的に`escape.json_encode`を呼び出してJSONに変換します。
class レスポンスハンドラ:
def 出力(self, データ):
if self._finished:
raise RuntimeError("finish()後にwrite()はできません")
if not isinstance(データ, (bytes, unicode_type, dict)):
raise TypeError("write()はbytes、unicode、dictのみを受け付けます")
if isinstance(データ, dict):
データ = escape.JSONエンコード(データ)
self.set_header("Content-Type", "application/json; charset=UTF-8")
データ = utf8(データ)
self._write_buffer.append(データ)
def JSONエンコード(value):
return json.dumps(value).replace("", "<\\/")
上記のコードからわかるように、`json_encode`関数は`json.dumps`を呼び出していますが、`ensure_ascii`パラメータを指定していないため、デフォルトの`True`が適用されます。これが、中国語文字が`\uXXXX`として出力される直接の原因です。
この問題を解決するには、明示的に`ensure_ascii=False`を指定してJSONをシリアライズする必要があります。
json.dumps(データ, ensure_ascii=False)
この修正により、JSONレスポンス内の中国語文字は正しく表示されるようになります。レガシープロジェクトではこの対応が必要ですが、新しいプロジェクトではPython 3の使用が推奨されます。