2013-03-30 9 views
22

Sono ancora un po 'nuovo a questo, quindi potrei non conoscere tutti i termini convenzionali per le cose:Preserve tuple Python con JSON

E' possibile conservare tuple Python durante la codifica con JSON? In questo momento json.loads(json.dumps(tuple)) mi restituisce una lista. Non voglio convertire le mie tuple in liste, ma voglio usare JSON. Quindi, ci sono opzioni?

Il motivo per cui: Sto creando un'app che utilizza array multidimensionali, non sempre la stessa forma. Ho alcuni metodi di classe che usano la ricorsione per sondare gli array e lanciare gli endpoint come stringa o int. Recentemente ho capito che (basato su come funziona la mia ricorsione) posso usare tuple per prevenire ricerche ricorsive più profonde degli array (Python rawks). Questo potrebbe tornare utile in situazioni in cui so di sicuro che non avrò bisogno di indagare più a fondo nelle mie strutture dati.

risposta

19

È possibile scrivere un encoder altamente specialzed e un gancio decoder:

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] 
      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

Nizza.Abbastanza simile a ciò che fa [pymongo] (https://github.com/mongodb/mongo-python-driver/blob/master/bson/json_util.py). Per essere completo, ci dovrebbe essere anche il ramo 'dict' in' encode'. – georg

+0

Ecco perché è specializzato :) Gli array di OP non sembrano avere dettati in loro. –

+0

Grazie! Mi ci è voluto un minuto per leggere il codice, ma ho capito e questo è esattamente ciò di cui avevo bisogno. È lo stesso modo in cui eseguo la ricorsione sugli array multi-d. Per il momento sto facendo ganci "al di fuori" di json, quindi forse dovrei leggere su 'object_hook's. – mrKelley

16

No, non è possibile. Non esiste un concetto di tupla nel formato JSON (vedere here per una suddivisione concisa di quali tipi esistono in JSON). Il modulo Python json converte le tuple Python in liste JSON perché questa è la cosa più simile in JSON a una tupla.

Qui non hai fornito molti dettagli del tuo caso d'uso, ma se hai bisogno di memorizzare rappresentazioni di stringhe di strutture dati che includono tuple, ti vengono in mente alcune possibilità, che possono essere o meno appropriate a seconda del tuo situazione:

  1. creare funzioni di codifica e decodifica
  2. usa pickle (attenzione; pickle.loads non è sicuro da usare su input fornito dall'utente).
  3. Utilizzare repr e ast.literal_eval anziché json.dumps e json.loads. repr fornirà un'uscita apparentemente simile a json.dumps, ma repr non convertirà le tuple in elenchi. ast.literal_eval è una versione meno potente e più sicura di eval che decodifica solo stringhe, numeri, tuple, elenchi, dadi, booleani e None.

L'opzione 3 è probabilmente la soluzione più semplice e più semplice per voi.

2

Il principio di differenza tra le liste di pitone e tuple è mutevolezza, che è irrilevante per rappresentazioni JSON, fintanto che non sta contemplando modifica dei membri interni dell'elenco JSON mentre è in forma di testo. Basta trasformare gli elenchi che torni in tuple. Se non si utilizzano decodificatori di oggetto personalizzati, gli unici tipi di dati strutturati da considerare sono gli oggetti e gli array JSON, che vengono visualizzati come dict e elenchi di python.

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 

Se si sta specializzando la decodifica, o vogliono alcuni array JSON siano liste Python e altri per essere tuple Python, è necessario avvolgere elementi di dati in un dict o tupla che annota le informazioni digitate. Questo di per sé è un modo migliore per influenzare il flusso di controllo di un algoritmo rispetto alla ramificazione basata sul fatto che qualcosa sia una lista o una tupla (o qualche altro tipo iterabile).

3

E 'con 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)