Jsonオブジェクト内の文字列を一括エンコード

Pythonjsonモジュールを使ってJson形式のファイルを読み込むと、その中の文字列はUnicode文字列として読み込まれる。

ただ、それだと困る時があって、例えばPythonからあるWindowsAPIを叩いて、そこに読み込んだ文字列を渡したいけど、そのAPIUnicode対応してないレガシーなものだった時とか(ニッチすぎる...)

プラットフォーム非依存にするためには、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