ソリューション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 Amery の機能は、これらのものよりも短くて明確ですが、それらのポイントは何ですか? なぜそれらを使用したいのですか?
純粋にパフォーマンスのため。マークの答えは、最初に JSON テキストをユニコード文字列で完全にデコードし、次にデコードされた値全体を再帰的に処理して、すべての文字列をバイト文字列に変換します。これには、いくつかの望ましくない影響があります。
- デコードされた構造全体のコピーがメモリに作成されます
- JSON オブジェクトが非常に深くネストされている場合( 500 レベル以上)、Python の最大再帰深度に達します。
この回答は、とのobject_hook
パラメーターを使用することで、これらのパフォーマンスの問題の両方を軽減します。ドキュメントから:json.load
json.loads
object_hook
デコードされたオブジェクト リテラル (a) の結果で呼び出されるオプションの関数ですdict
。の代わりに object_hook の戻り値が使用されますdict
。この機能を使用して、カスタム デコーダーを実装できます。
他の辞書の多くのレベルにネストされた辞書は、object_hook
デコードされたときに渡されるため、その時点でその中の文字列またはリストをバイト化し、後で深い再帰の必要性を回避できます。
object_hook
マークの答えは、ネストされた辞書に再帰するため、そのままでは使用に適していません。この回答では、ignore_dicts
パラメーター to を使用してその再帰を防ぎます。これは、新しいバイト化を渡す場合を除い_byteify
て、常に渡されます。フラグは、既にバイト化されているため、 sを無視するように指示します。object_hook
dict
ignore_dicts
_byteify
dict
最後に、デコードされる JSON テキストの最上位レベルにがない場合を処理するために、 orから返された結果に対してjson_load_byteified
andを(with で)json_loads_byteified
呼び出します。_byteify
ignore_dicts=True
json.load
json.loads
dict