我对此仍然有点新意,所以我可能不知道所有传统术语:
使用JSON编码时是否可以保留Python元组?现在json.loads(json.dumps(tuple))
给我一个清单。我不想将我的元组转换为列表,但我想使用JSON。那么,有选择吗?
原因: 我正在创建一个使用多维数组的应用程序,并不总是相同的形状。我有一些类方法使用递归来探测数组并将端点转换为字符串或int。我最近意识到(基于我的递归如何工作)我可以使用元组来防止更深入的数组递归搜索(Python rawks)。在我知道我肯定不需要深入研究我的数据结构的情况下,这可能会派上用场。
答案 0 :(得分:24)
您可以编写高度专业化的编码器和解码器钩子:
import json
class MultiDimensionalArrayEncoder(json.JSONEncoder):
def encode(self, obj):
def hint_tuples(item):
if isinstance(item, tuple):
return {'__tuple__': True, 'items': item}
if isinstance(item, list):
return [hint_tuples(e) for e in item]
if isinstance(item, dict):
return {key: hint_tuples(value) for key, value in item.items()}
else:
return item
return super(MultiDimensionalArrayEncoder, self).encode(hint_tuples(obj))
def hinted_tuple_hook(obj):
if '__tuple__' in obj:
return tuple(obj['items'])
else:
return obj
enc = MultiDimensionalArrayEncoder()
jsonstring = enc.encode([1, 2, (3, 4), [5, 6, (7, 8)]])
print jsonstring
# [1, 2, {"items": [3, 4], "__tuple__": true}, [5, 6, {"items": [7, 8], "__tuple__": true}]]
print json.loads(jsonstring, object_hook=hinted_tuple_hook)
# [1, 2, (3, 4), [5, 6, (7, 8)]]
答案 1 :(得分:19)
不,这是不可能的。 JSON格式中没有元组的概念(有关JSON中存在的类型的简要分析,请参阅here)。 Python的json
模块将Python元组转换为JSON列表,因为它是JSON中最接近元组的东西。
您还没有在此处详细说明您的用例,但如果您需要存储包含元组的数据结构的字符串表示,则会立即想到一些可能性,这可能是也可能不合适,具体取决于你的情况:
pickle.loads
对用户提供的输入无法安全使用。)repr
和ast.literal_eval
代替json.dumps
和json.loads
。 repr
会在外观上为json.dumps
提供合理相似的输出,但repr
不会将元组转换为列表。 ast.literal_eval
是一个功能较弱,更安全的eval
版本,它只会解码字符串,数字,元组,列表,字母,布尔值和None
。选项3可能是最简单,最简单的解决方案。
答案 2 :(得分:4)
用simplejson
import simplejson
def _to_json(python_object) :
if isinstance(python_object, tuple) :
python_object = {'__class__': 'tuple',
'__value__': list(python_object)}
else :
raise TypeError(repr(python_object) + ' is not JSON serializable')
return python_object
def _from_json(json_object):
if json_object['__class__'] == 'tuple':
return tuple(json_object['__value__'])
return json_object
jsn = simplejson.dumps((1,2,3),
default=_to_json,
tuple_as_array=False)
tpl = simplejson.loads(jsn, object_hook=_from_json)
答案 3 :(得分:3)
python列表和元组之间的主要区别在于可变性,这与JSON表示无关,只要您不考虑在文本形式中修改JSON列表的内部成员。你可以把你回来的列表变成元组。如果您不使用任何自定义对象解码器,您必须考虑的唯一结构化数据类型是JSON对象和数组,它们以python dicts和列表形式出现。
def tuplify(listything):
if isinstance(listything, list): return tuple(map(tuplify, listything))
if isinstance(listything, dict): return {k:tuplify(v) for k,v in listything.items()}
return listything
如果您专注于解码,或者希望某些JSON数组是python列表而其他JSON数组是python元组,那么您需要将数据项包装在注释类型信息的字典或元组中。这本身就是影响算法控制流的一种更好的方法,而不是基于某些东西是列表或元组(或其他可迭代类型)的分支。
答案 4 :(得分:0)
帕维尔·阿诺索夫(Pavel Anossov)很好地回答了这个问题。为了对诸如元组之类的对象进行编码,代码可以工作。将元组用作Python dict键也很有用,并且上面的代码无法将元组作为dict键处理。为了将元组作为键来管理,可以使用一个布尔标志来表示元组是否为字典键,并且元组将被包装在json.dumps(...)输出层中;在解码过程中,json由递归处理。
解决方案可以允许传递元组vs值的数据结构,从而使哈希更容易。 Python def __hash__(self):
经常返回对象中项的元组的哈希值,有时使未包装在类中的简单数据结构很有用。
__tuple__
进行模糊处理,以便在有人将字符串__tuple__
作为dict键的一部分进行编码的情况下,它可以通过编码器/解码器。下面的代码是我想出的用Python dict键编码元组的方法。 __main__
中包含一些基本测试,以演示该解决方案。放弃了编码输出的可读性,以增加通过解决方案的案例数量。
# 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,)
)
#"""