web-dev-qa-db-fra.com

Préserver Python avec JSON

Je suis encore un peu nouveau à ce sujet, donc je ne connais peut-être pas tous les termes conventionnels des choses:

Est-il possible de conserver Python tuples lors de l'encodage avec JSON? Pour l'instant, json.loads(json.dumps(Tuple)) me donne une liste. Je ne veux pas convertir mes tuples en listes, mais je voulez utiliser JSON. Alors, y a-t-il des options?

La raison pour laquelle: je crée une application qui utilise des tableaux multidimensionnels, pas toujours de la même forme. J'ai quelques méthodes de classe qui utilisent la récursivité pour sonder les tableaux et convertir les points de terminaison en chaîne ou en entier. J'ai récemment réalisé que (sur la base du fonctionnement de ma récursivité), je peux utiliser des tuples pour empêcher une recherche récursive plus approfondie des tableaux (Python rawks). Cela pourrait être utile dans des situations où je sais que je n'aurai certainement pas besoin d'approfondir mes structures de données.

26
mrKelley

Vous pouvez écrire un encodeur hautement spécialisé et un crochet de décodeur:

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)]]
28
Pavel Anossov

Non, ce n'est pas possible. Il n'y a pas de concept de Tuple au format JSON (voir ici pour une ventilation concise des types existants dans JSON). Le module json de Python convertit Python en listes JSON car c'est ce qui se rapproche le plus en JSON d'un Tuple.

Vous n'avez pas donné beaucoup de détails sur votre cas d'utilisation ici, mais si vous avez besoin de stocker des représentations de chaînes de structures de données qui incluent des tuples, quelques possibilités viennent immédiatement à l'esprit, qui peuvent ou non être appropriées selon votre situation:

  1. Créez vos propres fonctions d'encodage et de décodage
  2. Utilisez cornichon (attention; pickle.loads n'est pas sûr à utiliser sur les entrées fournies par l'utilisateur).
  3. Utilisez repr et ast.literal_eval au lieu de json.dumps et json.loads. repr vous donnera une sortie d'aspect raisonnablement similaire à json.dumps, mais repr ne convertira pas les tuples en listes. ast.literal_eval est une version moins puissante et plus sécurisée de eval qui décode uniquement les chaînes, les nombres, les tuples, les listes, les dict, les booléens et None.

L'option 3 est probablement la solution la plus simple et la plus simple pour vous.

19
Mark Amery

La principale différence entre python listes et tuples est la mutabilité, qui n'est pas pertinente pour les représentations JSON, tant que vous n'envisagez pas de modifier les membres internes de la liste JSON lorsqu'elle est sous forme de texte. Vous peut simplement transformer les listes que vous obtenez en tuples. Si vous n'utilisez pas de décodeurs d'objets personnalisés, les seuls types de données structurés que vous devez prendre en compte sont les objets et les tableaux JSON, qui se présentent sous la forme python dits et listes.

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

Si vous spécialisez le décodage ou si vous voulez que certains tableaux JSON soient python listes et d'autres python tuples, vous devrez encapsuler les éléments de données dans un dict ou un tuple qui annote les informations de type. C'est en soi un meilleur moyen d'influencer le flux de contrôle d'un algorithme que la ramification selon que quelque chose est une liste ou un tuple (ou un autre type itérable).

5
rhoark

C'est avec 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)
4
fgregg