2013-03-30 3 views
22

ich dies immer noch ein wenig neu bin, so dass ich vielleicht nicht alle herkömmlichen Begriffe für Dinge wissen:Preserve Python Tupeln mit JSON

Ist es möglich, Python Tupel zu erhalten, wenn sie mit JSON kodiert? Gerade jetzt json.loads(json.dumps(tuple)) gibt mir eine Liste zurück. Ich möchte meine Tupel nicht in Listen konvertieren, aber ich möchte JSON verwenden. Also, gibt es Optionen?

Der Grund warum: Ich erstelle eine App, die mehrdimensionale Arrays verwendet, nicht immer die gleiche Form. Ich habe einige Klassenmethoden, die Rekursion verwenden, um die Arrays zu untersuchen und die Endpunkte als String oder Int zu interpretieren. Ich habe kürzlich festgestellt, dass ich (basierend auf der Funktionsweise meiner Rekursion) Tupel verwenden kann, um tiefer rekursives Suchen von Arrays (Python-Rohlinge) zu verhindern. Dies könnte in Situationen nützlich sein, in denen ich sicher bin, dass ich nicht tiefer in meine Datenstrukturen eindringen muss.

Antwort

19

Sie einen hoch specialzed Encoder und einen Decoder Haken schreiben:

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.Ganz ähnlich wie [pymongo] (https://github.com/mongodb/mongo-python-driver/blob/master/bson/json_util.py). Um vollständig zu sein, sollte es auch einen 'dict'-Zweig in' encode' geben. – georg

+0

Deshalb, warum es spezialisiert ist :) OP-Arrays scheinen nicht dicts in ihnen. –

+0

Danke! Ich brauchte eine Minute, um den Code zu lesen, aber ich verstehe, und genau das brauchte ich. Das ist die gleiche Art, wie ich Rekursion für die Multi-D-Arrays mache. Ich mache gerade Haken "außerhalb von" Json, also sollte ich vielleicht auf 'object_hook's nachlesen. – mrKelley

16

Nein, das ist nicht möglich. Es gibt kein Konzept für ein Tupel im JSON-Format (siehe here für eine genaue Aufschlüsselung der in JSON vorhandenen Typen). Pythons Modul json konvertiert Python-Tupel in JSON-Listen, da dies in JSON einem Tupel am nächsten kommt.

Sie haben hier nicht viel Detail von Ihrem Anwendungsfall angegeben, aber wenn Sie String-Darstellungen von Datenstrukturen speichern müssen, die Tupel enthalten, kommen Ihnen sofort einige Möglichkeiten in den Sinn, die je nach Ihren Anforderungen möglicherweise nicht angemessen sind Situation:

  1. erstellen Sie Ihre eigenen Codierungs- und Decodierungsfunktionen
  2. Verwendung pickle (Vorsicht, pickle.loads nicht sicher ist, auf vom Benutzer bereitgestellte Eingang zu verwenden).
  3. Verwenden Sie repr und ast.literal_eval anstelle von und json.loads. repr gibt Ihnen eine Ausgabe, die dem Aussehen nach ähnlich aussieht wie , aber repr konvertiert Tupel nicht in Listen. ast.literal_eval ist eine weniger leistungsfähige, sicherere Version von eval, die nur Strings, Zahlen, Tupel, Listen, Dicts, Booleans und None dekodiert.

Option 3 ist wahrscheinlich die einfachste und einfachste Lösung für Sie.

2

Der prinzipielle Unterschied zwischen Python-Listen und Tupeln Wandelbarkeit, die JSON Darstellungen irrelevant ist, solange du nicht bist betrachtend Modifizieren der internen Mitglieder der JSON-Liste in ihrer Textform. Einfach die Listen in Tupel verwandeln. Wenn Sie keine benutzerdefinierten Objektdecoder verwenden, sind die einzigen strukturierten Datentypen, die Sie berücksichtigen müssen, JSON-Objekte und -Arrays, die als Python-Dicts und Listen ausgegeben werden.

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 

Wenn Sie die Decodierung spezialisieren, oder wollen einige JSON-Arrays Python-Listen und andere sein Python Tupel zu sein, müssen Sie Datenelemente in einem dict oder Tupel wickeln, die Informationen geben annotiert. Dies ist an sich eine bessere Möglichkeit, den Kontrollfluss eines Algorithmus zu beeinflussen als die Verzweigung basierend darauf, ob etwas eine Liste oder ein Tupel (oder ein anderer iterabler Typ) ist.

3

Es ist mit 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)