Mam Python set
, który zawiera obiekty z __hash__
i __eq__
metod w celu dokonania pewnych duplikatów nie są zawarte w zbiorze.
Muszę zakodować ten wynik w set
formacie json , ale przekazanie nawet pustego elementu set
do json.dumps
metody wywołuje TypeError
.
File "/usr/lib/python2.7/json/encoder.py", line 201, in encode
chunks = self.iterencode(o, _one_shot=True)
File "/usr/lib/python2.7/json/encoder.py", line 264, in iterencode
return _iterencode(o, 0)
File "/usr/lib/python2.7/json/encoder.py", line 178, in default
raise TypeError(repr(o) + " is not JSON serializable")
TypeError: set([]) is not JSON serializable
Wiem, że mogę utworzyć rozszerzenie json.JSONEncoder
klasy, która ma niestandardową default
metodę, ale nie jestem nawet pewien, od czego zacząć konwertowanie na set
. Czy powinienem utworzyć słownik z set
wartości w metodzie domyślnej, a następnie zwrócić kodowanie? Idealnie chciałbym, aby domyślna metoda była w stanie obsłużyć wszystkie typy danych, na których dusi oryginalny koder (używam Mongo jako źródła danych, więc daty wydają się również powodować ten błąd)
Każda wskazówka we właściwym kierunku będzie mile widziana.
EDYTOWAĆ:
Dziękuję za odpowiedź! Może powinienem był być bardziej precyzyjny.
Wykorzystałem (i przegłosowałem) odpowiedzi tutaj, aby ominąć ograniczenia set
tłumaczenia, ale istnieją również klucze wewnętrzne, które również stanowią problem.
Obiekty w pliku set
są złożonymi obiektami, które są tłumaczone na __dict__
, ale same mogą również zawierać wartości swoich właściwości, które mogą nie kwalifikować się do podstawowych typów w koderze json.
Wchodzi w to wiele różnych typów set
, a hash w zasadzie oblicza unikalny identyfikator jednostki, ale w prawdziwym duchu NoSQL nie można dokładnie określić, co zawiera obiekt potomny.
Jeden obiekt może zawierać wartość daty dla starts
, podczas gdy inny może mieć inny schemat, który nie zawiera kluczy zawierających obiekty „nieprymitywne”.
Dlatego jedynym rozwiązaniem, jakie przyszło mi do głowy, było rozszerzenie, JSONEncoder
aby zastąpić default
metodę włączania różnych przypadków - ale nie jestem pewien, jak się do tego zabrać, a dokumentacja jest niejednoznaczna. Czy w obiektach zagnieżdżonych wartość zwracana default
przechodzi przez klucz, czy jest to tylko ogólne dołączenie / odrzucenie, które patrzy na cały obiekt? W jaki sposób ta metoda obsługuje zagnieżdżone wartości? Przejrzałem poprzednie pytania i nie mogę znaleźć najlepszego podejścia do kodowania specyficznego dla przypadku (co niestety wydaje się być tym, co będę musiał tutaj zrobić).
źródło
dict
? Myślę, że chcesz po prostulist
wyjść z planu, a następnie przekazać to do enkodera ... np:encode(list(myset))
Odpowiedzi:
Notacja JSON ma tylko kilka rodzimych typów danych (obiekty, tablice, ciągi, liczby, wartości logiczne i null), więc wszystko, co jest serializowane w JSON, musi być wyrażone jako jeden z tych typów.
Jak pokazano w dokumentacji modułu json , ta konwersja może być wykonana automatycznie przez JSONEncoder i JSONDecoder , ale wtedy zrezygnujesz z innej struktury, której możesz potrzebować (jeśli przekonwertujesz zestawy na listę, stracisz możliwość regularnego odzyskiwania listy; jeśli przekonwertujesz zestawy na słownik za pomocą
dict.fromkeys(s)
, utracisz możliwość odzyskiwania słowników).Bardziej wyrafinowanym rozwiązaniem jest utworzenie niestandardowego typu, który może współistnieć z innymi natywnymi typami JSON. Pozwala to na przechowywanie zagnieżdżonych struktur, które obejmują listy, zbiory, dykty, liczby dziesiętne, obiekty daty i godziny itp .:
Oto przykładowa sesja pokazująca, że może obsługiwać listy, dykty i zestawy:
Alternatywnie przydatne może być użycie techniki serializacji bardziej ogólnego przeznaczenia, takiej jak YAML , Twisted Jelly lub Python's pickle module . Każdy z nich obsługuje znacznie większy zakres typów danych.
źródło
JSONDecoder
ale go nie używaMożesz utworzyć koder niestandardowy, który zwraca kod,
list
gdy napotka plikset
. Oto przykład:W ten sposób możesz również wykryć inne typy. Jeśli chcesz zachować, że lista była w rzeczywistości zestawem, możesz użyć niestandardowego kodowania. Coś takiego
return {'type':'set', 'list':list(obj)}
może zadziałać.Aby zilustrować zagnieżdżone typy, rozważ serializację tego:
Powoduje to następujący błąd:
Oznacza to, że koder pobierze
list
zwrócony wynik i rekurencyjnie wywoła serializator w swoich elementach podrzędnych. Aby dodać niestandardowy serializator dla wielu typów, możesz to zrobić:źródło
default
funkcja zostanie wywołana ponownie, tym razemobj
jako obiekt daty, więc wystarczy ją przetestować i zwrócić reprezentację daty.Dostosowałem rozwiązanie Raymonda Hettingera do Pythona 3.
Oto, co się zmieniło:
unicode
zniknąłdefault
zsuper()
base64
serializacjibytes
typu dostr
(ponieważ wydaje się, żebytes
w Pythonie 3 nie można przekonwertować na JSON)źródło
json.dumps()
powraca do / z'latin1'
, pomijającbase64
rzeczy, które nie są konieczne.W formacie JSON dostępne są tylko słowniki, listy i prymitywne typy obiektów (int, string, bool).
źródło
Nie musisz tworzyć niestandardowej klasy kodera, aby dostarczać
default
metodę - można ją przekazać jako argument słowa kluczowego:wyniki we
[1, 2, 3]
wszystkich obsługiwanych wersjach języka Python.źródło
Jeśli potrzebujesz tylko zakodować zestawy, a nie ogólne obiekty Pythona i chcesz, aby były one łatwe do odczytania przez człowieka, możesz użyć uproszczonej wersji odpowiedzi Raymonda Hettingera:
źródło
Jeśli potrzebujesz tylko szybkiego zrzutu i nie chcesz wdrażać niestandardowego kodera. Możesz użyć następujących:
json_string = json.dumps(data, iterable_as_array=True)
Spowoduje to przekonwertowanie wszystkich zestawów (i innych elementów iteracyjnych) na tablice. Uważaj tylko, że te pola pozostaną tablicami, gdy ponownie przeanalizujesz json. Jeśli chcesz zachować typy, musisz napisać niestandardowy koder.
źródło
Jedną z wad przyjętego rozwiązania jest to, że jego dane wyjściowe są bardzo specyficzne dla Pythona. Oznacza to, że jego surowe wyjście json nie może być obserwowane przez człowieka ani załadowane przez inny język (np. Javascript). przykład:
Dostaniesz:
Mogę zaproponować rozwiązanie, które obniża ocenę zestawu do dyktowania zawierającego listę po wyjściu i z powrotem do zestawu po załadowaniu do Pythona za pomocą tego samego kodera, zachowując w ten sposób obserwowalność i agnostycyzm językowy:
Co daje ci:
Uwaga że serializacja słownika, który ma element z kluczem
"__set__"
, zepsuje ten mechanizm. Więc__set__
teraz stał się zarezerwowanymdict
kluczem. Oczywiście możesz użyć innego, głębiej zaciemnionego klucza.źródło