2013-03-30 11 views
22

nadal jestem trochę nowy w tym, więc może nie znać wszystkie konwencjonalne warunki dla rzeczy:Zachowaj krotki Pythona z JSON

Czy to możliwe, aby zachować krotki Pythona podczas kodowania z JSON? W tej chwili json.loads(json.dumps(tuple)) daje mi listę. Nie chcę konwertować moich krotek na listy, ale chcę użyć JSON. Czy są jakieś opcje?

Powód: Tworzę aplikację korzystającą z wielowymiarowych macierzy, nie zawsze o tym samym kształcie. Mam kilka metod klasy, które używają rekurencji do sondowania tablic i rzutowania punktów końcowych jako ciąg lub int. Niedawno zdałem sobie sprawę, że (w oparciu o to, jak działa moja rekursja), mogę używać krotek, aby zapobiec głębszemu przeszukiwaniu rekurencyjnemu tablic (Python rawks). Może się to przydać w sytuacjach, w których wiem, że na pewno nie będę potrzebował zgłębiać głębiej moich struktur danych.

Odpowiedz

19

Można napisać wysoko specialzed koder i dekoder haczyk:

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

Nicea.Całkiem podobne do tego, co robi [pymongo] (https://github.com/mongodb/mongo-python-driver/blob/master/bson/json_util.py). Aby być kompletnym, powinna istnieć również gałąź 'dict' w' encode'. – georg

+0

To dlatego specjalizuje się :) Macierze OP nie mają w nich dyktatur. –

+0

Dzięki! Przeczytanie kodu zajęło mi minutę, ale otrzymałem go i właśnie tego potrzebowałem. W ten sam sposób robię rekursję na tablicach wielodrożnych. Robię teraz haki "poza" jsonem, więc może powinienem przeczytać na 'object_hook's. – mrKelley

16

Nie, to niemożliwe. Nie ma koncepcji krotki w formacie JSON (zobacz here, aby uzyskać krótki opis typów istniejących w JSON). Moduł Pythona json konwertuje krotki Pythona na listy JSON, ponieważ jest to najbliższa rzecz w JSON dla krotki.

Nie podałeś tu zbyt dużo szczegółów dotyczących użycia, ale jeśli potrzebujesz przechowywać ciągi reprezentacji struktur danych, które zawierają krotki, natychmiast pojawia się kilka możliwości, które mogą być lub nie są odpowiednie w zależności od twojego sytuacja:

  1. Tworzenie własnych funkcji kodowania i dekodowania
  2. Zastosowanie pickle (ostrożny; pickle.loads nie jest bezpieczny w użyciu na wejściu użytkownika na wyposażeniu).
  3. Użyj repr i ast.literal_eval zamiast json.dumps i json.loads. repr da ci wyjście dość podobne pod względem wyglądu do json.dumps, ale repr nie zamieni krotek na listy. ast.literal_eval to mniej wydajna, bezpieczniejsza wersja eval, która dekoduje tylko łańcuchy, liczby, krotki, listy, dyktandy, booleany i None.

Opcja 3 jest prawdopodobnie najłatwiejszym i najprostszym rozwiązaniem dla Ciebie.

2

Zasada różnica między listami Python i krotki jest zmienność, która nie ma znaczenia dla reprezentacji JSON, tak długo, jak nie jesteś kontemplując modyfikowanie wewnętrznych członków listy JSON, podczas gdy jest ona w formie tekstowej. Po prostu włącz listy, które otrzymasz z powrotem do krotek. Jeśli nie używasz żadnych niestandardowych dekoderów obiektów, jedynymi strukturalnymi typami danych, które musisz wziąć pod uwagę, są obiekty i tablice JSON, które pojawiają się jako dyktanda i listy Pythona.

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 

Jeśli specjalizujący dekodowanie, lub chcesz niektóre tablice JSON być list Python i innych za krotki python, trzeba owinąć elementy danych w dict lub krotki że annotates typu informacji. To samo w sobie jest lepszym sposobem wpływania na przepływ sterowania algorytmu niż rozgałęzienia w zależności od tego, czy coś jest listą lub krotką (lub innym typem iteracji).

3

Jest z 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)