web-dev-qa-db-fra.com

Python L'encodeur JSON convertit NaNs en 'null' à la place

J'écris du code pour recevoir un objet arbitraire (éventuellement imbriqué) capable d'être converti en JSON.

Le comportement par défaut de l'encodeur JSON intégré de Python est de convertir les NaN en NaN, par exemple json.dumps(np.NaN) résulte en NaN. Comment puis-je changer cette valeur NaN en 'null'?

J'ai essayé de sous-classe JSONEncoder et de remplacer la méthode default() comme suit:

from json import JSONEncoder, dumps
import numpy as np

class NanConverter(JSONEncoder):
    def default(self, obj):
        try:
            _ = iter(obj)
        except TypeError:
            if isinstance(obj, float) and np.isnan(obj):
                return "null"
        return JSONEncoder.default(self, obj)

>>> d = {'a': 1, 'b': 2, 'c': 3, 'e': np.nan, 'f': [1, np.nan, 3]}
>>> dumps(d, cls=NanConverter)
'{"a": 1, "c": 3, "b": 2, "e": NaN, "f": [1, NaN, 3]}'

RÉSULTAT ATTENDU: '{"a": 1, "c": 3, "b": 2, "e": null, "f": [1, null, 3]}'

27
Alexander

Cela semble atteindre mon objectif:

import simplejson


>>> simplejson.dumps(d, ignore_nan=True)
Out[3]: '{"a": 1, "c": 3, "b": 2, "e": null, "f": [1, null, 3]}'
20
Alexander

Malheureusement, vous devez probablement utiliser la suggestion de @ Bramar. Vous ne pourrez pas l'utiliser directement. La documentation pour l'encodeur JSON de Python indique:

Si spécifié, la valeur par défaut est une fonction qui est appelée pour les objets qui ne peuvent pas autrement être sérialisés

Votre NanConverter.default la méthode n'est même pas appelée, car l'encodeur JSON de Python sait déjà comment sérialiser np.nan. Ajoutez des instructions d'impression - vous verrez que votre méthode n'est même pas appelée.

7
Gerrat
  1. Comme le souligne @Gerrat, votre hook dumps(d, cls=NanConverter) ne fonctionnera malheureusement pas.

  2. La fonction simplejson.dumps(d, ignore_nan=True) d'Alexandre fonctionne mais introduit une dépendance supplémentaire (simplejson).

Si nous introduisons une autre dépendance (pandas):

  1. Une autre solution évidente serait dumps(pd.DataFrame(d).fillna(None)), mais Pandas issue 1972 note que d.fillna(None) aura un comportement imprévisible:

    Notez que fillna(None) est équivalent à fillna(), ce qui signifie que le paramètre de valeur n'est pas utilisé. Au lieu de cela, il utilise le paramètre de méthode qui est par défaut le remplissage direct.

  2. Donc, utilisez plutôt DataFrame.where:

    df = pd.DataFrame(d)
    dumps(df.where(pd.notnull(df), None)))
    
6
Michael Currie

Si vous travaillez avec pandas:

df = df.replace({pd.np.nan: None})

Le mérite revient à ce gars ici sur problème Github .

2
EliadL

simplejson fera le bon travail ici, mais il y a un indicateur supplémentaire qui vaut la peine d'être inclus:

Essayez d'utiliser simplejson:

pip install simplejson

Puis dans le code:

import simplejson

response = df.to_dict('records')
simplejson.dumps(response, ignore_nan=True,default=datetime.datetime.isoformat)

L'indicateur ignore_nan gérera correctement toutes les conversions NaN -> null

Le drapeau par défaut permettra à simplejson d'analyser correctement vos heures de données.

1
eiTan LaVi