Pavel Anossov answered the question well. To encode objects such as tuples the code works. Having tuples as Python dict keys is also useful, and the code above does not handle tuples as dict keys. To manage tuples as keys, a boolean flag signifying if tuple is a dict key can be used and tuple gets wrapped in a layer of json.dumps(...) output; during decode, json gets taken care of by recursion.
Solution can allow to pass data structures of tuple vs value that enables easier hashing. Python def __hash__(self):
frequently returns the hash of tuple of items in an object, and sometimes it is useful to have simpler data structures not wrapped in classes.
- hint_tuples can have a named argument dict_key -- flag for tuple being a dict key.
Python dict type cannot be a key to dict, better to turn it to a string with json.dumps(...), during decoding this should be restored to dict and recursion should take care of it being turned into tuple.
- optionally
__tuple__
can be obfuscated so that in case someone encodes a string __tuple__
as part of dict key it can pass through encoder / decoder.
Code below is what I came up with to take care of encoding tuples in Python dict keys. A couple of basic tests are included as part of __main__
to demonstrate the solution. Readability of encoding output is forgone to increase the number of cases that pass through the solution.
# Pavel Anossov's solution hinted this:
import json
tuple_signifier = '__tuple__s_i_g_n_i_f_i_e_r__'
class StreamTuple(dict):
def __hash__(self):
return hash(str(self))
class MultiDimensionalArrayEncoder(json.JSONEncoder):
def encode(self, obj):
def hint_tuples(item, dict_key=False):
global tuple_signifier
ret_val = None
if isinstance(item, tuple):
if dict_key:
ret_val = json.dumps(dict(
[(
tuple_signifier,
json.dumps(hint_tuples(list(item))),
),],
))
else:
ret_val = dict(
[(
tuple_signifier,
json.dumps(hint_tuples(list(item))),
),],
)
elif isinstance(item, list):
ret_val = [hint_tuples(e) for e in item]
elif isinstance(item, dict):
ret_val = dict([
(hint_tuples(key, dict_key=True), hint_tuples(value))
for key, value in item.items()
])
else:
ret_val = item
return ret_val
return super(MultiDimensionalArrayEncoder, self).\
encode(hint_tuples(obj))
def hinted_tuple_hook(obj):
global tuple_signifier
ret_val = {}
if tuple_signifier in obj:
ret_val = tuple(json.loads(obj[tuple_signifier], object_hook=hinted_tuple_hook,))
else:
for k, v in obj.items():
inner_k = k
inner_v = v
if isinstance(k, str) and tuple_signifier in k:
inner_k = json.loads(k, object_hook=hinted_tuple_hook,)
if isinstance(v, str) and tuple_signifier in v:
inner_v = json.loads(v, object_hook=hinted_tuple_hook,)
ret_val[inner_k] = inner_v
return ret_val
#
# Some tests that show how to use the above hinted tuple hook to encode
# / decode Python tuples.
#
if __name__ == '__main__':
enc = MultiDimensionalArrayEncoder()
test_input_1 = (2,)
test_input_2 = {(2,): 'a'}
test_input_3 = {'a': {(2,): {1:'a'}}}
print('test_input_1 encoded:', enc.encode(test_input_1), test_input_1)
print('test_input_1 decoded:',
json.loads(enc.encode(test_input_1),
object_hook=hinted_tuple_hook,)
)
#"""
print('test_input_2 encoded:', enc.encode(test_input_2))
print('test_input_2 decoded:',
json.loads(enc.encode(test_input_2),
object_hook=hinted_tuple_hook,)
)
print('\n' * 3)
print('test_input_3 encoded:', enc.encode(test_input_3))
print('test_input_3 decoded:',
json.loads(enc.encode(test_input_3),
object_hook=hinted_tuple_hook,)
)
print('\n' * 3)
test_input_4 = {'a': 'b'}
print('test_input_4 encoded:', enc.encode(test_input_4))
print('test_input_4 decoded:',
json.loads(enc.encode(test_input_4),
object_hook=hinted_tuple_hook,)
)
#"""
dict
branch inencode
. – Pascoe