TypeError: ObjectId (") n'est pas sérialisable JSON
ma réponse de MongoDB après avoir interrogé une fonction agrégée sur un document en utilisant Python, elle retourne une réponse valide et je peux l'imprimer mais ne peux pas la retourner.
erreur:
TypeError: ObjectId('51948e86c25f4b1d1c0d303c') is not JSON serializable
Impression:
{'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
il est 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
db est bien connecté et la collecte est là aussi et je suis de retour valide résultat attendu mais quand j'essaie de revenir, il me donne erreur Json. Une idée pour transformer la réponse en JOSON. Merci
9 réponses
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)
>>> 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 actuel de json_util .
contrairement à Jsonify de Flask, "dumps" retournera une chaîne, il ne peut donc pas être utilisé comme un remplacement 1:1 de jsonify de Flask.
mais cette question montre que nous pouvons sérialiser en utilisant json_util.dumps (), convertissez de nouveau en DCT en utilisant json.charges() et enfin appeler Flacon de jsonify sur elle.
exemple réponse à la question):
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 d'autres (C'est-à-dire binaire, Code, etc.) en un équivalent chaîne tel que" $oid."
sortie JSON ressemblerait à ceci:
{
"_id": {
"$oid": "abc123"
}
}
from bson import BSON
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)
ceci est l'exemple pour convertir BSON en objet JSON. Vous pouvez essayer cette.
pour un remplacement rapide, vous pouvez remplacer {'owner': objectid}
par {'owner': str(objectid)}
.
mais définir votre propre JSONEncoder
est une meilleure solution, cela dépend de vos besoins.
Voici comment j'ai récemment corrigé l'erreur
@app.route('/')
def home():
docs = []
for doc in db.person.find():
doc.pop('_id')
docs.append(doc)
return jsonify(docs)
je sais que je poste tard, mais j'ai pensé que cela aiderait au moins quelques personnes!
les Deux exemples mentionnés par tim et defuz(voté) fonctionne parfaitement bien. Cependant, il y a une différence infime qui pourrait être significative à certains moments.
- la méthode suivante ajoute un champ supplémentaire qui est redondant et peut ne pas être idéal dans tous les cas
Pymongo fournit json_util - vous pouvez utiliser celui-là à la place de gérer les types BSON
sortie: { "_ID": { "$oid": "abc123" } }
- où comme la classe JsonEncoder donne la même sortie dans le format de chaîne que nous avons besoin et nous avons besoin d'utiliser json.charges(sortie) en plus. Mais il conduit à
sortie: { "_id": "abc123" }
même si la première méthode semble simple, les deux méthodes nécessitent un effort minime.
Flacon de jsonify fournit à l'amélioration de la sécurité, comme décrit dans Sécurité JSON . Si l'encodeur personnalisé est utilisé avec le flacon, il est préférable de considérer points discutés dans le JSON Security
affichage ici que je pense qu'il peut être utile pour les gens qui utilisent Flask
avec pymongo
. Il s'agit de ma configuration actuelle "best practice" pour permettre flask à marshall pymongo BSON types de données.
mongoflask.py
from datetime import datetime, date
import isodate as iso
from bson import ObjectId
from flask.json import JSONEncoder
from werkzeug.routing import BaseConverter
class MongoJSONEncoder(JSONEncoder):
def default(self, o):
if isinstance(o, (datetime, date)):
return iso.datetime_isoformat(o)
if isinstance(o, ObjectId):
return str(o)
else:
return super().default(o)
class ObjectIdConverter(BaseConverter):
def to_python(self, value):
return ObjectId(value)
def to_url(self, value):
return str(value)
app.py
from .mongoflask import MongoJSONEncoder, ObjectIdConverter
def create_app():
app = Flask(__name__)
app.json_encoder = MongoJSONEncoder
app.url_map.converters['objectid'] = ObjectIdConverter
# Client sends their string, we interpret it as an ObjectId
@app.route('/users/<objectid:user_id>')
def show_user(user_id):
# setup not shown, pretend this gets us a pymongo db object
db = get_db()
# user_id is a bson.ObjectId ready to use with pymongo!
result = db.users.find_one({'_id': user_id})
# And jsonify returns normal looking json!
# {"_id": "5b6b6959828619572d48a9da",
# "name": "Will",
# "birthday": "1990-03-17T00:00:00Z"}
return jsonify(result)
return app
pourquoi au lieu de servir BSON ou mongod extended JSON ?
je pense que servir mongo special JSON met un fardeau sur les applications client. La plupart des applications client ne se soucieront pas de l'utilisation des objets mongo d'une manière complexe. Si je sers étendu json, j'ai maintenant l'utiliser côté serveur et côté client. ObjectId
et Timestamp
sont plus faciles à utiliser comme cordes et cela maintient toute cette folie de Mongo marshalling en quarantaine sur le serveur.
{
"_id": "5b6b6959828619572d48a9da",
"created_at": "2018-08-08T22:06:17Z"
}
je pense que c'est moins onéreux de travailler avec pour la plupart applications que.
{
"_id": {"$oid": "5b6b6959828619572d48a9da"},
"created_at": {"$date": 1533837843000}
}