Python JSON sérialiser un objet Decimal

242

J'ai un Decimal('3.9')dans le cadre d'un objet, et souhaite encoder cela en une chaîne JSON qui devrait ressembler à{'x': 3.9} . Je ne me soucie pas de la précision du côté client, donc un flotteur est très bien.

Existe-t-il un bon moyen de sérialiser cela? JSONDecoder n'accepte pas les objets Decimal, et la conversion en un flottant au préalable donne {'x': 3.8999999999999999}ce qui est faux, et sera un gros gaspillage de bande passante.

Knio
la source
2
bogue Python lié: l' encodeur json ne peut pas gérer la décimale
jfs
3.8999999999999999 n'est pas plus faux que 3.4. 0.2 n'a pas de représentation flottante exacte.
Jasen
@Jasen 3.89999999999 est environ 12,8% plus faux que 3,4. La norme JSON concerne uniquement la sérialisation et la notation, pas l'implémentation. L'utilisation d'IEEE754 ne fait pas partie de la spécification JSON brute, c'est seulement la façon la plus courante de l'implémenter. Une implémentation qui utilise uniquement une arithmétique décimale précise est complètement (en fait, encore plus strictement) conforme.
hraban
😂 moins mal. ironique.
hraban

Réponses:

147

Qu'en est-il du sous json.JSONEncoder- classement ?

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)

Ensuite, utilisez-le comme ceci:

json.dumps({'x': decimal.Decimal('5.5')}, cls=DecimalEncoder)
Michał Marczyk
la source
Aïe, je viens de remarquer que cela ne fonctionnera pas vraiment comme ça. Modifie en conséquence. (L'idée reste la même, cependant.)
Michał Marczyk
Le problème était qu'il DecimalEncoder()._iterencode(decimal.Decimal('3.9')).next()retournait le bon '3.9', mais DecimalEncoder()._iterencode(3.9).next()renvoyait un objet générateur qui ne reviendrait que '3.899...'lorsque vous en empileriez un autre .next(). Générateur d'affaires drôles. Oh bien ... Ça devrait marcher maintenant.
Michał Marczyk
8
Tu ne peux pas juste à la return (str(o),)place? [o]est une liste avec seulement 1 élément, pourquoi se donner la peine de boucler dessus?
mpen
2
@Mark: return (str(o),)retournerait un tuple de longueur 1, tandis que le code dans la réponse renvoie un générateur de longueur 1. Voir les documents iterencode ()
Abgan
30
Cette implémentation ne fonctionne plus. Celui d'Elias Zamaria est celui qui travaille sur le même style.
piro
224

Simplejson 2.1 et supérieur a un support natif pour le type décimal:

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

Notez que use_decimalc'est Truepar défaut:

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

Alors:

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

Espérons que cette fonctionnalité sera incluse dans la bibliothèque standard.

Lukas Cenovsky
la source
7
Hmm, pour moi, cela convertit les objets Decimal en flottants, ce qui n'est pas acceptable. Perte de précision lors de l'utilisation de devises, par exemple.
Matthew Schinckel
12
@MatthewSchinckel Je pense que non. Cela en fait une chaîne. Et si vous réinjectez la chaîne résultante, json.loads(s, use_decimal=True)vous obtenez la décimale. Aucun flotteur dans tout le processus. Modifié la réponse ci-dessus. J'espère que l'affiche originale va bien.
Shekhar
1
Aha, je pense que je n'utilisais pas non plus use_decimal=Trueles charges.
Matthew Schinckel
1
Pour moi json.dumps({'a' : Decimal('3.9')}, use_decimal=True)donne '{"a": 3.9}'. Le but n'était '{"a": "3.9"}'-il pas ?
MrJ
5
simplejson.dumps(decimal.Decimal('2.2'))fonctionne également: non explicite use_decimal(testé sur simplejson / 3.6.0). Une autre façon de le recharger est: json.loads(s, parse_float=Decimal)c'est-à-dire que vous pouvez le lire en utilisant stdlib json(et les anciennes simplejsonversions sont également prises en charge).
jfs
181

Je voudrais faire savoir à tout le monde que j'ai essayé la réponse de Michał Marczyk sur mon serveur Web qui exécutait Python 2.6.5 et cela a bien fonctionné. Cependant, j'ai mis à niveau vers Python 2.7 et il a cessé de fonctionner. J'ai essayé de penser à une sorte de moyen d'encoder des objets décimaux et voici ce que j'ai trouvé:

import decimal

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

J'espère que cela devrait aider toute personne ayant des problèmes avec Python 2.7. Je l'ai testé et cela semble bien fonctionner. Si quelqu'un remarque des bogues dans ma solution ou propose une meilleure solution, faites-le moi savoir.

Elias Zamaria
la source
4
Python 2.7 a changé les règles d'arrondi des flottants, donc cela fonctionne. Voir la discussion dans stackoverflow.com/questions/1447287/…
Nelson
2
Pour ceux d'entre nous qui ne peuvent pas utiliser simplejson (par exemple sur Google App Engine), cette réponse est une aubaine.
Joel Cross
17
Utilisez unicodeou strau lieu de floatpour assurer la précision.
Seppo Erviälä
2
Le problème avec 54.3999 ... était important dans Python 2.6.x et les versions antérieures où le flottant de conversion en chaîne ne fonctionnait pas régulièrement, mais la conversion Decimal en chaîne est beaucoup plus incorrecte car elle serait sérialisée en chaîne avec des guillemets doubles "54.4", pas en tant que un numéro.
hynekcer
1
Fonctionne en python3
SeanFromIT
43

Dans mon application Flask, qui utilise python 2.7.11, l'alchimie des flacons (avec les types 'db.decimal') et Flask Marshmallow (pour le sérialiseur et désérialiseur 'instantané'), j'ai eu cette erreur, chaque fois que j'ai fait un GET ou un POST . Le sérialiseur et le désérialiseur n'ont pas réussi à convertir les types décimaux en n'importe quel format identifiable JSON.

J'ai fait un "pip install simplejson", puis juste en ajoutant

import simplejson as json

le sérialiseur et le désérialiseur recommencent à ronronner. Je n'ai rien fait d'autre ... Les DEciamls sont affichés au format flottant '234.00'.

ISONecroMAn
la source
1
la solution la plus simple
SMDC
1
Curieusement, vous n'avez même pas besoin d'importer simplejson- il suffit de l'installer pour faire l'affaire. Initialement mentionné par cette réponse .
bsplosion
Cela ne fonctionne pas sur moi, et je l'ai Decimal('0.00') is not JSON serializable quand même après l'installation via pip. Cette situation se produit lorsque vous utilisez à la fois de la guimauve et du graphène. Lorsqu'une requête est appelée sur une API de repos, la guimauve fonctionne normalement pour les champs décimaux. Cependant, quand il est appelé avec graphql, il a généré une is not JSON serializableerreur.
Roel
Fantastic, Superb,
Spiderman
Parfait! Cela fonctionne dans les situations où vous utilisez un module écrit par quelqu'un d'autre que vous ne pouvez pas facilement modifier (dans mon cas, généralisé pour l'utilisation de Google Sheets)
happyskeptic
32

J'ai essayé de passer de simplejson à json intégré pour GAE 2.7 et j'ai eu des problèmes avec la décimale. Si la valeur par défaut renvoyait str (o), il y avait des guillemets (car _iterencode appelle _iterencode sur les résultats de la valeur par défaut), et float (o) supprimerait le 0 à la fin.

Si par défaut renvoie un objet d'une classe qui hérite de float (ou de tout ce qui appelle repr sans formatage supplémentaire) et possède une méthode __repr__ personnalisée, cela semble fonctionner comme je le souhaite.

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]'
tesdal
la source
Agréable! Cela garantit que la valeur décimale se retrouve dans le JSON en tant que flottant Javascript, sans que Python ne l'arrondisse d'abord à la valeur flottante la plus proche.
konrad
3
Malheureusement, cela ne fonctionne pas dans les Python 3 récents. Il existe maintenant un code d'accès rapide qui considère toutes les sous-classes float comme des flottants et n'appelle pas complètement repr sur elles.
Antti Haapala
@AnttiHaapala, l'exemple fonctionne très bien sur Python 3.6.
Cristian Ciupitu
@CristianCiupitu en effet, je ne semble pas être en mesure de reproduire le mauvais comportement maintenant
Antti Haapala
2
La solution a cessé de fonctionner depuis la v3.5.2rc1, voir github.com/python/cpython/commit/… . Il est float.__repr__codé en dur (qui perd en précision) et fakefloat.__repr__n'est pas appelé du tout. La solution ci-dessus fonctionne correctement pour python3 jusqu'à 3.5.1, si fakefloat a une méthode supplémentaire def __float__(self): return self.
myroslav
30

L'option native est manquante, je vais donc l'ajouter pour le prochain gars / gall qui le recherche.

À partir de Django 1.7.x, il existe un module intégré à partir duquel DjangoJSONEncodervous pouvez l'obtenir 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!

Javier Buzzi
la source
Bien que cela soit bon à savoir, le PO n'a pas posé de questions sur Django?
std''OrgnlDave
4
@ std''OrgnlDave vous avez 100% raison. J'ai oublié comment je suis arrivé ici, mais j'ai googlé cette question avec "django" attaché au terme de recherche et cela est apparu, après un peu plus de recherche sur Google, j'ai trouvé la réponse et l'ai ajoutée ici pour la prochaine personne comme moi, qui tombe sur il
Javier Buzzi
6
tu
sauves
14

Mon 0,02 $!

J'étends un tas de l'encodeur JSON depuis que je sérialise des tonnes de données pour mon serveur web. Voici un bon code. Notez qu'il est facilement extensible à pratiquement tous les formats de données que vous souhaitez et reproduira 3.9 comme"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

Rend ma vie tellement plus facile ...

std''OrgnlDave
la source
3
C'est incorrect: il reproduira 3.9 comme "thing": "3.9".
Glyph
les meilleures solutions de toutes, très simples, merci vous m'avez sauvé la journée, pour moi il suffit d'enregistrer le nombre, en chaîne pour la décimale c'est ok
stackdave
@Glyph via les standards JSON (dont il y en a quelques-uns ...), un nombre non cité est un point à virgule flottante double précision, pas un nombre décimal. Le citer est le seul moyen de garantir la compatibilité.
std''OrgnlDave
2
avez-vous une citation pour cela? Chaque spécification que j'ai lue implique que cela dépend de l'implémentation.
Glyph
12

3.9ne peut pas être représenté exactement dans les flotteurs IEEE, cela viendra toujours car 3.8999999999999999, par exemple, essayez print repr(3.9), vous pouvez en savoir plus à ce sujet ici:

http://en.wikipedia.org/wiki/Floating_point
http://docs.sun.com/source/806-3568/ncg_goldberg.html

Donc, si vous ne voulez pas de float, seule option vous devez l'envoyer sous forme de chaîne et pour permettre la conversion automatique des objets décimaux en JSON, faites quelque chose comme ceci:

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)
Anurag Uniyal
la source
Je sais que ce ne sera pas 3.9 en interne une fois analysé sur le client, mais 3.9 est un flotteur JSON valide. c'est-à-dire, json.loads("3.9")cela fonctionnera, et j'aimerais que ce soit ça
Knio
@Anurag Vous vouliez dire repr (obj) au lieu de repr (o) dans votre exemple.
orokusaki
Cela ne mourra-t-il pas simplement si vous essayez d'encoder quelque chose qui n'est pas décimal?
mikemaccana
1
@nailer, non, vous pouvez essayer cela, la raison étant la levée d'exception par défaut pour signaler que le prochain gestionnaire devrait être utilisé
Anurag Uniyal
1
Voir la réponse de mikez302 - en Python 2.7 ou supérieur, cela ne s'applique plus.
Joel Cross
9

Pour les utilisateurs de Django :

Récemment rencontré TypeError: Decimal('2337.00') is not JSON serializable lors de l'encodage JSON iejson.dumps(data)

Solution :

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

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

Mais, maintenant la valeur Decimal sera une chaîne, maintenant nous pouvons explicitement définir l'analyseur de valeur décimal / flottant lors du décodage des données, en utilisant l' parse_floatoption dans json.loads:

import decimal 

data = json.loads(data, parse_float=decimal.Decimal) # default is float(num_str)
Nabeel Ahmed
la source
8

Du document standard JSON , comme lié dans json.org :

JSON est agnostique sur la sémantique des nombres. Dans tout langage de programmation, il peut y avoir une variété de types de nombres de capacités et de compléments divers, fixes ou flottants, binaires ou décimaux. Cela peut rendre difficile l'échange entre différents langages de programmation. JSON n'offre à la place que la représentation des nombres que les humains utilisent: une séquence de chiffres. Tous les langages de programmation savent comment comprendre les séquences de chiffres même s'ils ne sont pas d'accord sur les représentations internes. Cela suffit pour permettre l'échange.

Il est donc exact de représenter les décimales sous forme de nombres (plutôt que de chaînes) dans JSON. Ci-dessous se trouve une solution possible au problème.

Définissez un encodeur JSON personnalisé:

import json


class CustomJsonEncoder(json.JSONEncoder):

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

Ensuite, utilisez-le lors de la sérialisation de vos données:

json.dumps(data, cls=CustomJsonEncoder)

Comme indiqué dans les commentaires sur les autres réponses, les anciennes versions de python peuvent perturber la représentation lors de la conversion en float, mais ce n'est plus le cas.

Pour récupérer la décimale en Python:

Decimal(str(value))

Cette solution est suggérée dans la documentation Python 3.0 sur les décimales :

Pour créer un Decimal à partir d'un flottant, convertissez-le d'abord en chaîne.

Hugo Mota
la source
2
Ce n'est pas «corrigé» dans Python 3. Conversion en un float nécessairement vous fait perdre la représentation décimale, et vous conduire à des écarts. Si Decimalc'est important à utiliser, je pense qu'il vaut mieux utiliser des cordes.
juanpa.arrivillaga
Je pense que c'est sûr de le faire depuis python 3.1. La perte de précision peut être nuisible dans les opérations arithmétiques, mais dans le cas du codage JSON, vous produisez simplement un affichage de chaîne de la valeur, donc la précision est plus que suffisante pour la plupart des cas d'utilisation. Tout dans JSON est déjà une chaîne, donc mettre des guillemets autour de la valeur défie simplement la spécification JSON.
Hugo Mota
Cela dit, je comprends les préoccupations liées à la conversion en flottant. Il existe probablement une stratégie différente à utiliser avec l'encodeur pour produire la chaîne d'affichage souhaitée. Pourtant, je ne pense pas que cela vaille la peine de produire une valeur cotée.
Hugo Mota
@HugoMota "Tout dans JSON est déjà une chaîne, donc mettre des guillemets autour de la valeur défie la spécification JSON." Non: rfc-editor.org/rfc/rfc8259.txt - JSON est un format d'encodage basé sur du texte, mais cela ne signifie pas que tout ce qu'il contient doit être interprété comme une chaîne. La spécification définit comment coder les nombres, séparément des chaînes.
Gunnar Þór Magnússon
@ GunnarÞórMagnússon "JSON est un format d'encodage basé sur du texte" - c'est ce que je voulais dire par "tout est une chaîne". La conversion des nombres en chaîne au préalable ne préservera pas comme par magie la précision car ce sera de toute façon une chaîne lorsqu'elle deviendra JSON. Et selon les spécifications, les chiffres n'ont pas de guillemets autour. C'est la responsabilité du lecteur de préserver la précision lors de la lecture (pas une citation, juste mon point de vue).
Hugo Mota
6

Voici ce que j'ai, extrait de notre classe

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)

Qui passe le moins:

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)
James Lin
la source
json.loads(myString, cls=CommonJSONEncoder)commentaire devrait êtrejson.loads(myString, cls=CommonJSONDecoder)
Can Kavaklıoğlu
object_hook a besoin d'une valeur de retour par défaut si obj n'est pas décimal.
Can Kavaklıoğlu
3

Vous pouvez créer un encodeur JSON personnalisé selon vos besoins.

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)

Le décodeur peut être appelé comme ceci,

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

et la sortie sera:

>>'{"x": 3.9}'
moineau
la source
génial ... Merci pour une solution unique (y)
basilic muhammed
Ça marche vraiment! Merci de partager votre solution
tthreetorch
3

Pour ceux qui ne veulent pas utiliser une bibliothèque tierce ... Un problème avec la réponse d'Elias Zamaria est qu'elle se transforme en float, ce qui peut entraîner des problèmes. Par exemple:

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

La JSONEncoder.encode()méthode vous permet de retourner le contenu json littéral, contrairement à JSONEncoder.default()ce qui vous oblige à retourner un type compatible json (comme float) qui est ensuite encodé de la manière normale. Le problème encode()est qu'il ne fonctionne (normalement) qu'au niveau supérieur. Mais c'est toujours utilisable, avec un peu de travail supplémentaire (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)

Ce qui vous donne:

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

Sur la base de la réponse stdOrgnlDave, j'ai défini ce wrapper qu'il peut être appelé avec des types facultatifs afin que l'encodeur ne fonctionne que pour certains types à l'intérieur de vos projets. Je crois que le travail doit être fait à l'intérieur de votre code et ne pas utiliser cet encodeur "par défaut" car "il vaut mieux explicite qu'implicite", mais je comprends que son utilisation vous fera gagner du temps. :-)

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()
Juanmi Taboada
la source
0

Si vous souhaitez passer un dictionnaire contenant des décimales à la requestsbibliothèque (en utilisant l' jsonargument mot - clé), il vous suffit d'installer 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')})

La raison du problème est qu'il requestsn'utilise simplejsonque s'il est présent, et retombe sur le intégré jsons'il n'est pas installé.

Max Malysh
la source
-6

cela peut être fait en ajoutant

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

dans \Lib\json\encoder.py:JSONEncoder._iterencode, mais j'espérais une meilleure solution

Knio
la source
5
Vous pouvez sous-classer JSONEncoder comme illustré ci-dessus, l'édition des fichiers Python installés d'une bibliothèque établie ou l'interpréteur lui-même devrait être un tout dernier recours.
justanr