Python JSON serializuje obiekt dziesiętny


242

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.


2
powiązany błąd w Pythonie: koder json nie może obsłużyć dziesiętnych
— jfs

3,899999999999999999 nie jest bardziej błędny niż 3,4. 0.2 nie ma dokładnej reprezentacji pływaka.
— Jasen

@Jasen 3.89999999999 jest około 12,8% bardziej błędny niż 3,4. Standard JSON dotyczy tylko serializacji i notacji, a nie implementacji. Używanie IEEE754 nie jest częścią surowej specyfikacji JSON, jest tylko najczęstszym sposobem jej implementacji. Implementacja wykorzystująca tylko precyzyjną arytmetykę dziesiętną jest całkowicie (a nawet ściślej) zgodna.
— hraban

😂 mniej źle. ironiczny.
— hraban

Odpowiedzi:


147

Co powiesz na podklasę json.JSONEncoder?

class DecimalEncoder(json.JSONEncoder):
    def _iterencode(self, o, markers=None):
        if isinstance(o, decimal.Decimal):
            # wanted a simple yield str(o) in the next line,
            # but that would mean a yield on the line with super(...),
            # which wouldn't work (see my comment below), so...
            return (str(o) for o in [o])
        return super(DecimalEncoder, self)._iterencode(o, markers)

Następnie użyj go w następujący sposób:

json.dumps({'x': decimal.Decimal('5.5')}, cls=DecimalEncoder)

Właśnie zauważyłem, że tak naprawdę to nie będzie działać. Będzie odpowiednio edytować. (Pomysł pozostaje ten sam.)
— Michał Marczyk,

Problem polegał na tym, że DecimalEncoder()._iterencode(decimal.Decimal('3.9')).next()zwrócił poprawny '3.9', ale DecimalEncoder()._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ć.
— Michał Marczyk,

8
Nie możesz po prostu return (str(o),)zamiast tego? [o]jest listą zawierającą tylko 1 element, po co zawracać sobie nią głowę?
— mpen

2
@Mark: 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 ()
— Abgan

30
Ta implementacja już nie działa. Jeden Elias Zamaria pracuje nad tym samym stylem.
— piro

223

Simplejson 2.1 i nowsze mają natywną obsługę typu dziesiętnego:

>>> json.dumps(Decimal('3.9'), use_decimal=True)
'3.9'

Należy pamiętać, że use_decimaljest Truedomyślnie:

def dumps(obj, skipkeys=False, ensure_ascii=True, check_circular=True,
    allow_nan=True, cls=None, indent=None, separators=None,
    encoding='utf-8', default=None, use_decimal=True,
    namedtuple_as_object=True, tuple_as_array=True,
    bigint_as_string=False, sort_keys=False, item_sort_key=None,
    for_json=False, ignore_nan=False, **kw):

Więc:

>>> json.dumps(Decimal('3.9'))
'3.9'

Mamy nadzieję, że ta funkcja będzie zawarta w standardowej bibliotece.


7
Hmm, dla mnie to przekształca obiekty dziesiętne na liczby zmiennoprzecinkowe, co jest niedopuszczalne. Na przykład utrata precyzji podczas pracy z walutą.
— Matthew Schinckel

12
@MatthewSchinckel Myślę, że nie. W rzeczywistości tworzy z niego ciąg. A jeśli podasz wynikowy ciąg z powrotem do 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.
— Shekhar

1
Aha, myślę, że też nie używałem use_decimal=Trueobciążeń.
— Matthew Schinckel,

1
Dla mnie json.dumps({'a' : Decimal('3.9')}, use_decimal=True)daje '{"a": 3.9}'. Czy cel nie był '{"a": "3.9"}'?
— MrJ

5
simplejson.dumps(decimal.Decimal('2.2'))działa również: brak wyraźnych use_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ą stdlib json(i simplejsonobsługiwane są również stare wersje).
— jfs

181

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:

import decimal

class DecimalEncoder(json.JSONEncoder):
    def default(self, o):
        if isinstance(o, decimal.Decimal):
            return float(o)
        return super(DecimalEncoder, self).default(o)

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ć.


4
Python 2.7 zmienił zasady zaokrąglania liczb zmiennoprzecinkowych, więc to działa. Zobacz dyskusję w stackoverflow.com/questions/1447287/…
— Nelson

2
Dla tych z nas, którzy nie mogą korzystać z simplejson (np. W Google App Engine), ta odpowiedź jest wybawieniem.
— Joel Cross

17
Użyj unicodelub strzamiast, floataby zapewnić precyzję.
— Seppo Erviälä

2
Problem z 54.3999 ... był ważny w Pythonie 2.6.x i starszych, gdzie konwersja zmiennoprzecinkowa na łańcuch nie działała regularnie, ale konwersja dziesiętna na łańcuch jest znacznie bardziej niepoprawna, ponieważ byłaby serializowana jako łańcuch z podwójnymi cudzysłowami "54.4", a nie jako numer.
— hynekcer

1
Działa w python3
— SeanFromIT

43

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

import simplejson as json

serializator i deserializator znów zaczynają mruczeć. Nie zrobiłem nic innego ... DEciamls są wyświetlane jako format zmiennoprzecinkowy „234,00”.


1
najłatwiejsza poprawka
— SMDC

1
Co dziwne, nie musisz nawet importować simplejson- wystarczy sama instalacja. Początkowo wspomniany w tej odpowiedzi .
— bsplosion

To nie działa na mnie, a mimo to dostałem to 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 serializablebłąd.
— Roel,

Fantastyczne, wspaniałe,
— Spiderman

Idealny! Działa to w sytuacjach, gdy używasz modułu napisanego przez kogoś innego, którego nie możesz łatwo zmodyfikować (w moim przypadku gspread do korzystania z Arkuszy Google)
— happyskeptic

32

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ę.

import json
from decimal import Decimal

class fakefloat(float):
    def __init__(self, value):
        self._value = value
    def __repr__(self):
        return str(self._value)

def defaultencode(o):
    if isinstance(o, Decimal):
        # Subclass float with custom repr?
        return fakefloat(o)
    raise TypeError(repr(o) + " is not JSON serializable")

json.dumps([10.20, "10.20", Decimal('10.20')], default=defaultencode)
'[10.2, "10.20", 10.20]'

Miły! Dzięki temu wartość dziesiętna kończy się w JSON jako zmiennoprzecinkowa Javascript, bez uprzedniego zaokrąglenia go przez Python do najbliższej wartości zmiennoprzecinkowej.
— konrad

3
Niestety nie działa to w najnowszych Python 3. Istnieje teraz kod szybkiej ścieżki, który traktuje wszystkie podklasy float jako zmiennoprzecinkowe i nie wywołuje na nich repr.
— Antti Haapala

@AnttiHaapala, przykład działa dobrze na Pythonie 3.6.
— Cristian Ciupitu

@CristianCiupitu, rzeczywiście, nie wydaje mi się, że mogę teraz odtworzyć złe zachowanie
— Antti Haapala,

2
Rozwiązanie przestało działać od wersji 3.5.2rc1, patrz github.com/python/cpython/commit/… . Jest float.__repr__zakodowany (który traci precyzję) i fakefloat.__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.
— myroslav

30

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.

import json
from django.core.serializers.json import DjangoJSONEncoder
from django.forms.models import model_to_dict

model_instance = YourModel.object.first()
model_dict = model_to_dict(model_instance)

json.dumps(model_dict, cls=DjangoJSONEncoder)

Presto!


Chociaż dobrze jest wiedzieć, że OP nie pytało o Django?
— std''OrgnlDave

4
@ std''OrgnlDave masz 100% poprawność. Zapomniałem, jak się tu dostałem, ale przeszukałem to pytanie z „django” dołączonym do wyszukiwanego hasła i to pojawiło się, po trochę google, znalazłem odpowiedź i dodałem ją tutaj dla następnej osoby takiej jak ja, która natknęła się na it
— Javier Buzzi


14

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

JSONEncoder_olddefault = json.JSONEncoder.default
def JSONEncoder_newdefault(self, o):
    if isinstance(o, UUID): return str(o)
    if isinstance(o, datetime): return str(o)
    if isinstance(o, time.struct_time): return datetime.fromtimestamp(time.mktime(o))
    if isinstance(o, decimal.Decimal): return str(o)
    return JSONEncoder_olddefault(self, o)
json.JSONEncoder.default = JSONEncoder_newdefault

Ułatwia mi życie ...


3
Jest to niepoprawne: odtworzy 3,9 jako "thing": "3.9".
— Glyph

najlepsze rozwiązania ze wszystkich, bardzo proste, dzięki, że uratowałeś mi dzień, dla mnie wystarczy, aby zapisać liczbę, ciąg znaków dziesiętnych jest w porządku
— stackdave

@Glyph za pośrednictwem standardów JSON (których jest kilka ...), niecytowana liczba jest zmiennoprzecinkową podwójnej precyzji, a nie liczbą dziesiętną. Cytując to jedyny sposób na zagwarantowanie zgodności.
— std''OrgnlDave

2
masz na to powód? Każda specyfikacja, którą przeczytałem, sugeruje, że jest zależna od implementacji.
— Glyph

12

3.9nie może być dokładnie reprezentowany w pływakach IEEE, zawsze przyjdzie, ponieważ 3.8999999999999999np. spróbuj print 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:

import decimal
from django.utils import simplejson

def json_encode_decimal(obj):
    if isinstance(obj, decimal.Decimal):
        return str(obj)
    raise TypeError(repr(obj) + " is not JSON serializable")

d = decimal.Decimal('3.5')
print simplejson.dumps([d], default=json_encode_decimal)

Wiem, że nie będzie to 3,9 wewnętrznie po przeanalizowaniu go na kliencie, ale 3,9 to poprawna zmienna JSON. tzn. json.loads("3.9")zadziała i chciałbym, żeby tak było
— Knio

@Anurag Miałeś na myśli repr (obj) zamiast repr (o) w twoim przykładzie.
— orokusaki

Czy to nie umrze, jeśli spróbujesz zakodować coś, co nie jest dziesiętne?
— mikemaccana

1
@nailer, nie, nie będzie, możesz tego spróbować, ponieważ domyślnie podniesiono wyjątek, aby zasygnalizować, że należy użyć następnego
— modułu

1
Zobacz odpowiedź mikez302 - w Pythonie 2.7 lub nowszym nie ma to już zastosowania.
— Joel Cross

9

Dla użytkowników Django :

Ostatnio natknąłem się na TypeError: Decimal('2337.00') is not JSON serializable kodowanie JSON tjjson.dumps(data)

Rozwiązanie :

# converts Decimal, Datetime, UUIDs to str for Encoding
from django.core.serializers.json import DjangoJSONEncoder  

json.dumps(response.data, cls=DjangoJSONEncoder)

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_floatopcji w json.loads:

import decimal 

data = json.loads(data, parse_float=decimal.Decimal) # default is float(num_str)

8

Ze standardowego dokumentu JSON , podlinkowanego w json.org :

JSON jest agnostykiem co do semantyki liczb. W dowolnym języku programowania może istnieć wiele różnych typów różnych pojemności i uzupełnień, stałych lub zmiennych, binarnych lub dziesiętnych. Może to utrudnić wymianę między różnymi językami programowania. Zamiast tego JSON oferuje tylko reprezentację liczb używanych przez ludzi: ciąg cyfr. Wszystkie języki programowania wiedzą, jak zrozumieć sekwencje cyfr, nawet jeśli nie zgadzają się co do wewnętrznych reprezentacji. To wystarczy, aby umożliwić wymianę.

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:

import json


class CustomJsonEncoder(json.JSONEncoder):

    def default(self, obj):
        if isinstance(obj, Decimal):
            return float(obj)
        return super(CustomJsonEncoder, self).default(obj)

Następnie użyj go podczas serializacji danych:

json.dumps(data, cls=CustomJsonEncoder)

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:

Decimal(str(value))

To rozwiązanie jest wskazane w dokumentacji języka dziesiętnego w języku Python 3.0 :

Aby utworzyć liczbę dziesiętną z liczby zmiennoprzecinkowej, najpierw przekonwertuj ją na ciąg.


2
To nie jest „stałe” w Pythonie 3. konwersji na float zawsze sprawia, że można stracić reprezentację dziesiętną, i będzie prowadzić do rozbieżności. Jeśli Decimalużycie jest ważne, myślę, że lepiej jest używać ciągów znaków.
— juanpa.arrivillaga

Uważam, że jest to bezpieczne, odkąd python 3.1. Utrata precyzji może być szkodliwa w operacjach arytmetycznych, ale w przypadku kodowania JSON, po prostu generujesz ciąg wyświetlania wartości, więc precyzja jest więcej niż wystarczająca dla większości przypadków użycia. Wszystko w JSON jest już ciągiem, więc umieszczanie cudzysłowów wokół wartości po prostu przeczy specyfikacji JSON.
— Hugo Mota,

Powiedziawszy to, rozumiem obawy związane z przejściem na pływak. Prawdopodobnie istnieje inna strategia do użycia z koderem w celu uzyskania pożądanego ciągu wyświetlania. Mimo to nie wydaje mi się, żeby warto było podawać wartość.
— Hugo Mota,

@ HugoMota „Wszystko w JSON jest już ciągiem, więc umieszczanie cudzysłowów wokół wartości po prostu przeczy specyfikacji JSON”. Nie: rfc-editor.org/rfc/rfc8259.txt - JSON to tekstowy format kodowania, ale to nie znaczy, że wszystko w nim należy interpretować jako ciąg znaków. Specyfikacja określa sposób kodowania liczb, niezależnie od ciągów.
— Gunnar Þór Magnússon

@ GunnarÞórMagnússon „JSON to tekstowy format kodowania” - o to mi chodziło z „wszystko jest ciągiem znaków”. Wcześniejsze przekształcenie liczb na ciąg nie magicznie zachowa precyzji, ponieważ i tak stanie się ciągiem, gdy stanie się JSON. Zgodnie ze specyfikacją liczby nie zawierają cudzysłowów. Obowiązkiem czytelnika jest zachowanie precyzji podczas czytania (nie cytat, tylko moje zdanie).
— Hugo Mota

6

To właśnie otrzymałem z naszej klasy

class CommonJSONEncoder(json.JSONEncoder):

    """
    Common JSON Encoder
    json.dumps(myString, cls=CommonJSONEncoder)
    """

    def default(self, obj):

        if isinstance(obj, decimal.Decimal):
            return {'type{decimal}': str(obj)}

class CommonJSONDecoder(json.JSONDecoder):

    """
    Common JSON Encoder
    json.loads(myString, cls=CommonJSONEncoder)
    """

    @classmethod
    def object_hook(cls, obj):
        for key in obj:
            if isinstance(key, six.string_types):
                if 'type{decimal}' == key:
                    try:
                        return decimal.Decimal(obj[key])
                    except:
                        pass

    def __init__(self, **kwargs):
        kwargs['object_hook'] = self.object_hook
        super(CommonJSONDecoder, self).__init__(**kwargs)

Który przechodzi nieprzystosowany:

def test_encode_and_decode_decimal(self):
    obj = Decimal('1.11')
    result = json.dumps(obj, cls=CommonJSONEncoder)
    self.assertTrue('type{decimal}' in result)
    new_obj = json.loads(result, cls=CommonJSONDecoder)
    self.assertEqual(new_obj, obj)

    obj = {'test': Decimal('1.11')}
    result = json.dumps(obj, cls=CommonJSONEncoder)
    self.assertTrue('type{decimal}' in result)
    new_obj = json.loads(result, cls=CommonJSONDecoder)
    self.assertEqual(new_obj, obj)

    obj = {'test': {'abc': Decimal('1.11')}}
    result = json.dumps(obj, cls=CommonJSONEncoder)
    self.assertTrue('type{decimal}' in result)
    new_obj = json.loads(result, cls=CommonJSONDecoder)
    self.assertEqual(new_obj, obj)

json.loads(myString, cls=CommonJSONEncoder)komentarz powinien byćjson.loads(myString, cls=CommonJSONDecoder)
— Can Kavaklıoğlu,

hook_obiektu wymaga domyślnej wartości zwracanej, jeśli obiekt nie jest dziesiętny.
— Can Kavaklıoğlu

3

Możesz utworzyć niestandardowy koder JSON zgodnie ze swoimi wymaganiami.

import json
from datetime import datetime, date
from time import time, struct_time, mktime
import decimal

class CustomJSONEncoder(json.JSONEncoder):
    def default(self, o):
        if isinstance(o, datetime):
            return str(o)
        if isinstance(o, date):
            return str(o)
        if isinstance(o, decimal.Decimal):
            return float(o)
        if isinstance(o, struct_time):
            return datetime.fromtimestamp(mktime(o))
        # Any other serializer if needed
        return super(CustomJSONEncoder, self).default(o)

Dekoder można tak nazwać,

import json
from decimal import Decimal
json.dumps({'x': Decimal('3.9')}, cls=CustomJSONEncoder)

i wynik będzie:

>>'{"x": 3.9}'

super ... Dzięki za rozwiązanie one stop (y)
— muhammed basil

To naprawdę działa! Dziękujemy za udostępnienie rozwiązania
— tthreetorch

3

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:

>>> json.dumps({'x': Decimal('0.0000001')}, cls=DecimalEncoder)
'{"x": 1e-07}'
>>> json.dumps({'x': Decimal('100000000000.01734')}, cls=DecimalEncoder)
'{"x": 100000000000.01733}'

JSONEncoder.encode()Metoda pozwala powrócić literalną treść json, w przeciwieństwie JSONEncoder.default(), który wrócisz typ zgodny json (jak pływaka), który następnie zostaje zakodowany w normalny sposób. Problem encode()polega na tym, że (normalnie) działa tylko na najwyższym poziomie. Ale nadal jest użyteczny, z odrobiną dodatkowej pracy (python 3.x):

import json
from collections.abc import Mapping, Iterable
from decimal import Decimal

class DecimalEncoder(json.JSONEncoder):
    def encode(self, obj):
        if isinstance(obj, Mapping):
            return '{' + ', '.join(f'{self.encode(k)}: {self.encode(v)}' for (k, v) in obj.items()) + '}'
        if isinstance(obj, Iterable) and (not isinstance(obj, str)):
            return '[' + ', '.join(map(self.encode, obj)) + ']'
        if isinstance(obj, Decimal):
            return f'{obj.normalize():f}'  # using normalize() gets rid of trailing 0s, using ':f' prevents scientific notation
        return super().encode(obj)

Co daje ci:

>>> json.dumps({'x': Decimal('0.0000001')}, cls=DecimalEncoder)
'{"x": 0.0000001}'
>>> json.dumps({'x': Decimal('100000000000.01734')}, cls=DecimalEncoder)
'{"x": 100000000000.01734}'

2

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. :-)

import time
import json
import decimal
from uuid import UUID
from datetime import datetime

def JSONEncoder_newdefault(kind=['uuid', 'datetime', 'time', 'decimal']):
    '''
    JSON Encoder newdfeault is a wrapper capable of encoding several kinds
    Use it anywhere on your code to make the full system to work with this defaults:
        JSONEncoder_newdefault()  # for everything
        JSONEncoder_newdefault(['decimal'])  # only for Decimal
    '''
    JSONEncoder_olddefault = json.JSONEncoder.default

    def JSONEncoder_wrapped(self, o):
        '''
        json.JSONEncoder.default = JSONEncoder_newdefault
        '''
        if ('uuid' in kind) and isinstance(o, uuid.UUID):
            return str(o)
        if ('datetime' in kind) and isinstance(o, datetime):
            return str(o)
        if ('time' in kind) and isinstance(o, time.struct_time):
            return datetime.fromtimestamp(time.mktime(o))
        if ('decimal' in kind) and isinstance(o, decimal.Decimal):
            return str(o)
        return JSONEncoder_olddefault(self, o)
    json.JSONEncoder.default = JSONEncoder_wrapped

# Example
if __name__ == '__main__':
    JSONEncoder_newdefault()

0

Jeśli chcesz przekazać słownik zawierający dziesiętne do requestsbiblioteki (używając jsonargumentu słowa kluczowego), wystarczy zainstalować simplejson:

$ pip3 install simplejson    
$ python3
>>> import requests
>>> from decimal import Decimal
>>> # This won't error out:
>>> requests.post('https://www.google.com', json={'foo': Decimal('1.23')})

Przyczyną problemu jest to, że requestsużywa simplejsontylko wtedy, gdy jest obecny, i wraca do wbudowanego, jsonjeśli nie jest zainstalowany.


-6

można to zrobić poprzez dodanie

    elif isinstance(o, decimal.Decimal):
        yield str(o)

w \Lib\json\encoder.py:JSONEncoder._iterencode, ale miałem nadzieję na lepsze rozwiązanie


5
Możesz podklasować JSONEncoder jak na przykładzie powyżej, edytowanie zainstalowanych plików Pythona z ustalonej biblioteki lub samego interpretera powinno być ostatecznością.
— justanr
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.