Python 2におけるJSONレスポンスの中国語文字がUnicodeエスケープシーケンスで表示される問題

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の使用が推奨されます。

タグ: Python2 JSON Tornado Unicode エンコーディング

6月4日 18:11 投稿