146 votes

Erreur de type : ObjectId('') n'est pas sérialisable en JSON

Ma réponse en retour de MongoDB après avoir interrogé une fonction agrégée sur le document en utilisant Python, il retourne une réponse valide et je peux l'imprimer mais pas la retourner.

Erreur :

TypeError: ObjectId('51948e86c25f4b1d1c0d303c') is not JSON serializable

Imprimer :

{'result': [{'_id': ObjectId('51948e86c25f4b1d1c0d303c'), 'api_calls_with_key': 4, 'api_calls_per_day': 0.375, 'api_calls_total': 6, 'api_calls_without_key': 2}], 'ok': 1.0}

Mais quand j'essaie de revenir :

TypeError: ObjectId('51948e86c25f4b1d1c0d303c') is not JSON serializable

C'est un appel RESTfull :

@appv1.route('/v1/analytics')
def get_api_analytics():
    # get handle to collections in MongoDB
    statistics = sldb.statistics

    objectid = ObjectId("51948e86c25f4b1d1c0d303c")

    analytics = statistics.aggregate([
    {'$match': {'owner': objectid}},
    {'$project': {'owner': "$owner",
    'api_calls_with_key': {'$cond': [{'$eq': ["$apikey", None]}, 0, 1]},
    'api_calls_without_key': {'$cond': [{'$ne': ["$apikey", None]}, 0, 1]}
    }},
    {'$group': {'_id': "$owner",
    'api_calls_with_key': {'$sum': "$api_calls_with_key"},
    'api_calls_without_key': {'$sum': "$api_calls_without_key"}
    }},
    {'$project': {'api_calls_with_key': "$api_calls_with_key",
    'api_calls_without_key': "$api_calls_without_key",
    'api_calls_total': {'$add': ["$api_calls_with_key", "$api_calls_without_key"]},
    'api_calls_per_day': {'$divide': [{'$add': ["$api_calls_with_key", "$api_calls_without_key"]}, {'$dayOfMonth': datetime.now()}]},
    }}
    ])

    print(analytics)

    return analytics

La base de données est bien connectée et la collection est là aussi. J'ai obtenu le résultat valide attendu mais quand j'essaie de retourner le résultat, cela me donne une erreur Json. Avez-vous une idée de la façon de reconvertir la réponse en JSON ? Merci

187voto

tim Points 585

Pymongo fournit json_util - vous pouvez utiliser celui-là à la place pour gérer les types BSON

def parse_json(data):
    return json.loads(json_util.dumps(data))

160voto

defuz Points 5575

Vous devez définir votre propre JSONEncoder et l'utiliser :

import json
from bson import ObjectId

class JSONEncoder(json.JSONEncoder):
    def default(self, o):
        if isinstance(o, ObjectId):
            return str(o)
        return json.JSONEncoder.default(self, o)

JSONEncoder().encode(analytics)

Il est également possible de l'utiliser de la manière suivante.

json.encode(analytics, cls=JSONEncoder)

49voto

Garren S Points 2553
>>> from bson import Binary, Code
>>> from bson.json_util import dumps
>>> dumps([{'foo': [1, 2]},
...        {'bar': {'hello': 'world'}},
...        {'code': Code("function x() { return 1; }")},
...        {'bin': Binary("")}])
'[{"foo": [1, 2]}, {"bar": {"hello": "world"}}, {"code": {"$code": "function x() { return 1; }", "$scope": {}}}, {"bin": {"$binary": "AQIDBA==", "$type": "00"}}]'

Exemple réel de json_util .

Contrairement à jsonify de Flask, "dumps" retournera une chaîne, donc il ne peut pas être utilisé comme un remplacement 1:1 de jsonify de Flask.

Pero cette question montre que nous pouvons sérialiser en utilisant json_util.dumps(), reconvertir en dict en utilisant json.loads() et enfin appeler jsonify de Flask.

Exemple (dérivé de la réponse à la question précédente) :

from bson import json_util, ObjectId
import json

#Lets create some dummy document to prove it will work
page = {'foo': ObjectId(), 'bar': [ObjectId(), ObjectId()]}

#Dump loaded BSON to valid JSON string and reload it as dict
page_sanitized = json.loads(json_util.dumps(page))
return page_sanitized

Cette solution convertira ObjectId et autres (ie Binary, Code, etc) en un équivalent de chaîne de caractères tel que "$oid".

La sortie JSON ressemblerait à ceci :

{
  "_id": {
    "$oid": "abc123"
  }
}

38voto

A-B-B Points 797

La plupart des utilisateurs qui reçoivent l'erreur "not JSON serializable" doivent simplement spécifier default=str lors de l'utilisation de json.dumps . Par exemple :

json.dumps(my_obj, default=str)

Cela forcera une conversion en str pour éviter l'erreur. Bien sûr, regardez ensuite la sortie générée pour confirmer qu'elle correspond à ce dont vous avez besoin.

24voto

vinit kantrod Points 1016
from bson import json_util
import json

@app.route('/')
def index():
    for _ in "collection_name".find():
        return json.dumps(i, indent=4, default=json_util.default)

Voici un exemple de conversion de BSON en objet JSON. Vous pouvez essayer ceci.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X