Mam Decimal('3.9')
jako część obiektu i chcę zakodować to w ciągu JSON, który powinien wyglądać {'x': 3.9}
. Nie dbam o precyzję po stronie klienta, więc float jest w porządku.
Czy istnieje dobry sposób na serializację tego? JSONDecoder nie akceptuje obiektów dziesiętnych, a wcześniejsza konwersja do {'x': 3.8999999999999999}
liczby zmiennoprzecinkowej daje błędne wyniki i będzie dużym marnotrawstwem przepustowości.
Odpowiedzi:
Co powiesz na podklasę
json.JSONEncoder
?Następnie użyj go w następujący sposób:
źródło
DecimalEncoder()._iterencode(decimal.Decimal('3.9')).next()
zwrócił poprawny'3.9'
, aleDecimalEncoder()._iterencode(3.9).next()
zwrócił obiekt generatora, który zwróciłby się tylko'3.899...'
wtedy, gdy ułożyłeś stos na innym.next()
. Generator zabawny biznes. No cóż ... Teraz powinno działać.return (str(o),)
zamiast tego?[o]
jest listą zawierającą tylko 1 element, po co zawracać sobie nią głowę?return (str(o),)
zwróci krotkę o długości 1, a kod w odpowiedzi zwróci generator długości 1. Zobacz dokumentację iterencode ()Simplejson 2.1 i nowsze mają natywną obsługę typu dziesiętnego:
Należy pamiętać, że
use_decimal
jestTrue
domyślnie:Więc:
Mamy nadzieję, że ta funkcja będzie zawarta w standardowej bibliotece.
źródło
json.loads(s, use_decimal=True)
niego, otrzymasz z powrotem dziesiętną liczbę. Brak ruchu w całym procesie. Edytowano powyżej odpowiedzi. Mam nadzieję, że oryginalny plakat jest w porządku.use_decimal=True
obciążeń.json.dumps({'a' : Decimal('3.9')}, use_decimal=True)
daje'{"a": 3.9}'
. Czy cel nie był'{"a": "3.9"}'
?simplejson.dumps(decimal.Decimal('2.2'))
działa również: brak wyraźnychuse_decimal
(testowane na simplejson / 3.6.0). Innym sposobem na załadowanie go z powrotem jest:json.loads(s, parse_float=Decimal)
tzn. Możesz go odczytać za pomocą stdlibjson
(isimplejson
obsługiwane są również stare wersje).Chciałbym poinformować wszystkich, że wypróbowałem odpowiedź Michała Marczyka na moim serwerze WWW, na którym działał Python 2.6.5 i działało dobrze. Jednak zaktualizowałem do Pythona 2.7 i przestał działać. Próbowałem wymyślić jakiś sposób na zakodowanie obiektów dziesiętnych i oto co wymyśliłem:
Mam nadzieję, że powinno to pomóc każdemu, kto ma problemy z Python 2.7. Przetestowałem to i wydaje się, że działa dobrze. Jeśli ktoś zauważy jakieś błędy w moim rozwiązaniu lub wymyśli lepszy sposób, daj mi znać.
źródło
unicode
lubstr
zamiast,float
aby zapewnić precyzję."54.4"
, a nie jako numer.W mojej aplikacji Flask, która używa Pythona 2.7.11, alchemii kolby (z typami „db.decimal”) i Flask Marshmallow (dla serializatora „natychmiastowego” i deserializatora), miałem ten błąd, za każdym razem, gdy robiłem GET lub POST . Serializator i deserializator nie mógł przekonwertować typów dziesiętnych na żaden możliwy do zidentyfikowania format JSON.
Zrobiłem „pip install simplejson”, a potem po prostu dodając
serializator i deserializator znów zaczynają mruczeć. Nie zrobiłem nic innego ... DEciamls są wyświetlane jako format zmiennoprzecinkowy „234,00”.
źródło
simplejson
- wystarczy sama instalacja. Początkowo wspomniany w tej odpowiedzi .Decimal('0.00') is not JSON serializable
po zainstalowaniu przez pip. Taka sytuacja występuje, gdy używasz zarówno pianki piankowej, jak i grafenu. Gdy zapytanie jest wywoływane w interfejsie API spoczynku, pianka prawidłowa działa w przypadku pól dziesiętnych. Jednak po wywołaniu z grafql zgłosiłis not JSON serializable
błąd.Próbowałem przejść z simplejson na wbudowany json dla GAE 2.7 i miałem problemy z przecinkiem. Jeśli domyślnie zwrócił str (o), istniały cudzysłowy (ponieważ _iterencode wywołuje _iterencode na wynikach domyślnych), a float (o) usuwa końcowe 0.
Jeśli default zwraca obiekt klasy, który dziedziczy po float (lub cokolwiek, co wywołuje repr bez dodatkowego formatowania) i ma niestandardową metodę __repr__, wydaje się, że działa tak, jak tego chcę.
źródło
float.__repr__
zakodowany (który traci precyzję) ifakefloat.__repr__
wcale nie jest wywoływany. Powyższe rozwiązanie działa poprawnie dla Pythona do wersji 3.5.1, jeśli fakefloat ma dodatkową metodędef __float__(self): return self
.Brakuje natywnej opcji, więc dodam ją dla następnego faceta / żółcia, który jej szuka.
Począwszy od wersji Django 1.7.x jest wbudowana funkcja
DjangoJSONEncoder
, z której można go pobraćdjango.core.serializers.json
.Presto!
źródło
Moje 0,02 $!
Rozszerzam wiązkę kodera JSON, ponieważ serializuję mnóstwo danych dla mojego serwera WWW. Oto fajny kod. Pamiętaj, że można go łatwo rozszerzyć na dowolny format danych i odtworzysz 3.9 as
"thing": 3.9
Ułatwia mi życie ...
źródło
"thing": "3.9"
.3.9
nie może być dokładnie reprezentowany w pływakach IEEE, zawsze przyjdzie, ponieważ3.8999999999999999
np. spróbujprint repr(3.9)
, możesz przeczytać więcej o tym tutaj:http://en.wikipedia.org/wiki/Floating_point
http://docs.sun.com/source/806-3568/ncg_goldberg.html
Jeśli więc nie chcesz liczb zmiennoprzecinkowych, tylko opcję musisz wysłać jako ciąg znaków i aby umożliwić automatyczną konwersję obiektów dziesiętnych na JSON, wykonaj coś takiego:
źródło
json.loads("3.9")
zadziała i chciałbym, żeby tak byłoDla użytkowników Django :
Ostatnio natknąłem się na
TypeError: Decimal('2337.00') is not JSON serializable
kodowanie JSON tjjson.dumps(data)
Rozwiązanie :
Ale teraz wartość dziesiętna będzie ciągiem, teraz możemy jawnie ustawić parser wartości dziesiętnej / zmiennoprzecinkowej podczas dekodowania danych, używając
parse_float
opcji wjson.loads
:źródło
Ze standardowego dokumentu JSON , podlinkowanego w json.org :
Tak więc w JSON dokładne jest przedstawianie Dziesiętnych jako liczb (a nie ciągów znaków). Poniżej znajduje się możliwe rozwiązanie problemu.
Zdefiniuj niestandardowy koder JSON:
Następnie użyj go podczas serializacji danych:
Jak zauważono w komentarzach do innych odpowiedzi, starsze wersje Pythona mogą zepsuć reprezentację podczas konwersji na zmiennoprzecinkowe, ale tak już nie jest.
Aby odzyskać przecinek dziesiętny w Pythonie:
To rozwiązanie jest wskazane w dokumentacji języka dziesiętnego w języku Python 3.0 :
źródło
float
zawsze sprawia, że można stracić reprezentację dziesiętną, i będzie prowadzić do rozbieżności. JeśliDecimal
użycie jest ważne, myślę, że lepiej jest używać ciągów znaków.To właśnie otrzymałem z naszej klasy
Który przechodzi nieprzystosowany:
źródło
json.loads(myString, cls=CommonJSONEncoder)
komentarz powinien byćjson.loads(myString, cls=CommonJSONDecoder)
Możesz utworzyć niestandardowy koder JSON zgodnie ze swoimi wymaganiami.
Dekoder można tak nazwać,
i wynik będzie:
źródło
Dla tych, którzy nie chcą korzystać z biblioteki innej firmy ... Problem z odpowiedzią Eliasa Zamarii polega na tym, że konwertuje się ona na float, co może powodować problemy. Na przykład:
JSONEncoder.encode()
Metoda pozwala powrócić literalną treść json, w przeciwieństwieJSONEncoder.default()
, który wrócisz typ zgodny json (jak pływaka), który następnie zostaje zakodowany w normalny sposób. Problemencode()
polega na tym, że (normalnie) działa tylko na najwyższym poziomie. Ale nadal jest użyteczny, z odrobiną dodatkowej pracy (python 3.x):Co daje ci:
źródło
Na podstawie odpowiedzi stdOrgnlDave zdefiniowałem to opakowanie, że można go wywoływać z opcjonalnymi rodzajami, aby koder działał tylko dla niektórych rodzajów w twoich projektach. Uważam, że praca powinna zostać wykonana wewnątrz twojego kodu i nie należy używać tego „domyślnego” kodera, ponieważ „jest on bardziej jawny niż niejawny”, ale rozumiem, że użycie tego pozwoli zaoszczędzić trochę twojego czasu. :-)
źródło
Jeśli chcesz przekazać słownik zawierający dziesiętne do
requests
biblioteki (używającjson
argumentu słowa kluczowego), wystarczy zainstalowaćsimplejson
:Przyczyną problemu jest to, że
requests
używasimplejson
tylko wtedy, gdy jest obecny, i wraca do wbudowanego,json
jeśli nie jest zainstalowany.źródło
można to zrobić poprzez dodanie
w
\Lib\json\encoder.py:JSONEncoder._iterencode
, ale miałem nadzieję na lepsze rozwiązanieźródło