1. ホーム
  2. パイソン

[解決済み】JSONからUnicodeの代わりに文字列オブジェクトを取得する方法は?

2022-03-30 05:50:46

質問

を使っています。 Python 2 からのJSONをパースするために ASCIIエンコード テキストファイルです。

これらのファイルを json または simplejson の場合、すべての文字列値は文字列オブジェクトではなく、Unicodeオブジェクトにキャストされます。問題は、文字列オブジェクトしか受け付けないいくつかのライブラリでデータを使用しなければならないことです。I ライブラリは変更できない また、更新もできません。

Unicodeではなく、文字列のオブジェクトを取得することは可能でしょうか?

>>> import json
>>> original_list = ['a', 'b']
>>> json_list = json.dumps(original_list)
>>> json_list
'["a", "b"]'
>>> new_list = json.loads(json_list)
>>> new_list
[u'a', u'b']  # I want these to be of type `str`, not `unicode`

更新情報

この質問は だいぶ前 に行き詰まった時、「このままではいけない。 Python 2 . 今日の簡単できれいな解決策の一つは、最近のバージョンのPythonを使うことです - すなわち パイソン3 と進む。

どのように解決するのですか?

による解決策 object_hook

[編集]: Python 2.7用に更新 3.xとの互換性。

import json

def json_load_byteified(file_handle):
    return _byteify(
        json.load(file_handle, object_hook=_byteify),
        ignore_dicts=True
    )

def json_loads_byteified(json_text):
    return _byteify(
        json.loads(json_text, object_hook=_byteify),
        ignore_dicts=True
    )

def _byteify(data, ignore_dicts = False):
    if isinstance(data, str):
        return data

    # if this is a list of values, return list of byteified values
    if isinstance(data, list):
        return [ _byteify(item, ignore_dicts=True) for item in data ]
    # if this is a dictionary, return dictionary of byteified keys and values
    # but only if we haven't already byteified it
    if isinstance(data, dict) and not ignore_dicts:
        return {
            _byteify(key, ignore_dicts=True): _byteify(value, ignore_dicts=True)
            for key, value in data.items() # changed to .items() for python 2.7/3
        }

    # python 3 compatible duck-typing
    # if this is a unicode string, return its string representation
    if str(type(data)) == "<type 'unicode'>":
        return data.encode('utf-8')

    # if it's anything else, return it in its original form
    return data

使用例です。

>>> json_loads_byteified('{"Hello": "World"}')
{'Hello': 'World'}
>>> json_loads_byteified('"I am a top-level string"')
'I am a top-level string'
>>> json_loads_byteified('7')
7
>>> json_loads_byteified('["I am inside a list"]')
['I am inside a list']
>>> json_loads_byteified('[[[[[[[["I am inside a big nest of lists"]]]]]]]]')
[[[[[[[['I am inside a big nest of lists']]]]]]]]
>>> json_loads_byteified('{"foo": "bar", "things": [7, {"qux": "baz", "moo": {"cow": ["milk"]}}]}')
{'things': [7, {'qux': 'baz', 'moo': {'cow': ['milk']}}], 'foo': 'bar'}
>>> json_load_byteified(open('somefile.json'))
{'more json': 'from a file'}

この仕組みと、なぜ使うのか?

マーク・エイメリーの機能 は、これらよりも短くてわかりやすいので、何が言いたいのか?なぜ、それらを使いたいのですか?

純粋に パフォーマンス . Markの回答は、まずJSONテキストをunicode文字列で完全にデコードし、次にデコードされた値全体を再帰してすべての文字列をバイト文字列に変換しています。これには、いくつかの望ましくない効果があります。

  • デコードされた構造体全体のコピーがメモリ上に作成されます。
  • もし、JSONオブジェクトが 本当に 深いネスト(500レベル以上)になると、Pythonの最大再帰深度にぶつかります。

この回答では、これらのパフォーマンスの問題を緩和するために object_hook のパラメータは json.loadjson.loads . から ドキュメント :

object_hook はオプションの関数で、オブジェクトリテラルをデコードした結果で呼び出されます。 dict ). object_hook の返り値が dict . この機能は、カスタムデコーダの実装に使用できます。

他の辞書の何階層もネストした辞書は object_hook デコードされると そのため、この時点で内部の文字列やリストをバイト化し、後で深い再帰処理を行う必要をなくすことができます。

マークの回答は object_hook このままでは、ネストした辞書に再帰してしまうからです。この回答では、再帰を防ぐために ignore_dicts パラメータを _byteify 常に渡される ただし いつ object_hook を渡すと、新しい dict をバイト化する。その ignore_dicts フラグは _byteify を無視するために dict は既にバイト化されているので

最後に、私たちが実装している json_load_byteifiedjson_loads_byteified コール _byteifyignore_dicts=True から返された結果に対して json.load または json.loads を使うことで、デコードされるJSONテキストに dict をトップレベルにしています。