Jsonオブジェクト内の文字列を一括エンコード
Pythonのjsonモジュールを使ってJson形式のファイルを読み込むと、その中の文字列はUnicode文字列として読み込まれる。
ただ、それだと困る時があって、例えばPythonからあるWindowsAPIを叩いて、そこに読み込んだ文字列を渡したいけど、そのAPIがUnicode対応してないレガシーなものだった時とか(ニッチすぎる...)
プラットフォーム非依存にするためには、Unicode形式に統一するのがよいのだけど、内部処理する際に困ることがありまして...
また、Jsonの構成がネスト状になっていた時に、含まれているオブジェクトに対して一括でエンコードするにはどうしたらよいか調べてみたら、StackOverFlowで議論されてました。
How to get string objects instead of Unicode ones from JSON in Python?
jso.load()を呼ぶ際、object_hookに関数を渡すとコールバックされるので、これを利用する。
以下のコードでは、パースされるとdecode_dictが呼ばれる。また対象のオブジェクトがリストの場合はdecode_dictからdecode_listが呼ばれ、その値がUnicode形式ならエンコードします。
object_hookについては、Pythonの公式ドキュメントに詳細が書かれてます。
object_hook は、もし指定されれば、全てのデコードされた JSON オブジェクトに対して呼ばれその返値は与えられた dict の代わりに使われます。
http://docs.python.jp/2.7/library/json.html
この機能は独自の脱直列化 (たとえば JSON-RPC クラスヒンティングをサポートするような) を提供するのに使えます。
def _decode_list(data): rv = [] for item in data: if isinstance(item, unicode): item = item.encode('utf-8') elif isinstance(item, list): item = _decode_list(item) elif isinstance(item, dict): item = _decode_dict(item) rv.append(item) return rv def _decode_dict(data): rv = {} for key, value in data.iteritems(): if isinstance(key, unicode): key = key.encode('utf-8') if isinstance(value, unicode): value = value.encode('utf-8') elif isinstance(value, list): value = _decode_list(value) elif isinstance(value, dict): value = _decode_dict(value) rv[key] = value return rv def deserialize_json(json_str): try: # encode the json object 'unicode' string to 'str' string json_obj = json.loads(json_str, object_hook=_decode_dict) except IOError as (ecode, emsg): d = os.path.split(path)[0] print('[Error](%d)%s [%s]' % (ecode, emsg, d), file=sys.stderr) sys.exit(1) def load_json(path): b = os.path.exists(path) if b == False: return None try: with open(path, 'r') as f: json_str = f.read() json_obj = deserialize_json(json_str) except IOError as (ecode, emsg): json_obj = None print ('Error: (%d)%s' % (ecode, emsg), file=sys.stderr) return json_obj