MongoAlchemy document Encode à JSON via Flask-MongoAlchemy
Question
Je pense que je manque un petit quelque chose ici. Je teste en Python cadre Flask et Flask-MongoAlchemy, et que vous souhaitez convertir une entité en JSON. Voici mon code (Abstraite):
from flask import Flask
from flaskext.mongoalchemy import MongoAlchemy
try:
from bson.objectid import ObjectId
except:
pass
#a bunch of code to open the mongoDB
class ClassA(db.Document):
title = db.StringField()
field1 = db.StringField()
field2 = db.BoolField()
@app.route('/api/classA', methods=['GET'])
def api_list_all
a = ClassA.query.all()
result = []
for b in a:
result.append(b.wrap())
print result
return json.dumps(result)
Sans la ligne json.dumps, l'instruction d'impression rapide le bon résultat. Mais seulement si je lance le json.dumps sur le résultat, il donne:
TypeError: ObjectId ( '...') n'est pas JSON sérialisable
Qu'est-ce que je manque?
La solution
Le résultat est un document mongo d'une sorte qui contient le contenu de type ObjectId, que vous aurez à dire JSON comment désérialiser. Vous aurez le même problème avec d'autres types de spécifiques mongo, comme ReferenceField (), EmbeddedDocumentField (), etc. Vous devez écrire une fonction de désérialisation que vous pouvez passer à JSON. Ce que j'utilise est:
def encode_model(obj, recursive=False):
if obj is None:
return obj
if isinstance(obj, (mongoengine.Document, mongoengine.EmbeddedDocument)):
out = dict(obj._data)
for k,v in out.items():
if isinstance(v, ObjectId):
if k is None:
out['_id'] = str(v)
del(out[k])
else:
# Unlikely that we'll hit this since ObjectId is always NULL key
out[k] = str(v)
else:
out[k] = encode_model(v)
elif isinstance(obj, mongoengine.queryset.QuerySet):
out = encode_model(list(obj))
elif isinstance(obj, ModuleType):
out = None
elif isinstance(obj, groupby):
out = [ (g,list(l)) for g,l in obj ]
elif isinstance(obj, (list)):
out = [encode_model(item) for item in obj]
elif isinstance(obj, (dict)):
out = dict([(k,encode_model(v)) for (k,v) in obj.items()])
elif isinstance(obj, datetime.datetime):
out = str(obj)
elif isinstance(obj, ObjectId):
out = {'ObjectId':str(obj)}
elif isinstance(obj, (str, unicode)):
out = obj
elif isinstance(obj, float):
out = str(obj)
else:
raise TypeError, "Could not JSON-encode type '%s': %s" % (type(obj), str(obj))
return out
Ensuite, vous auriez traiter le résultat comme:
return json.dumps(result, default=encode_model)
ou quelque chose à cet effet.
Autres conseils
Vous pouvez également utiliser la méthode query.raw_output()
d'avoir ce retour d'instance de requête dictionnaires Python premières au lieu d'un objet Python. Avec un dictionnaire, il devient facile de coder à l'aide JSON json.dumps()
:
import json
q=db.query(MyObject)
q.raw_output()
json.dumps(q.first())
Référence http://www.mongoalchemy.org /api/expressions/query.html#mongoalchemy.query.Query.raw_output
En combinant les deux réponses précédentes, vous devriez être en mesure de faire quelque chose comme ceci:
from bson import json_util
# ...
@app.route('/api/classA', methods=['GET'])
def api_list_all
a = ClassA.query.all()
result = []
for b in a:
result.append(b.wrap())
print result
return json_utils.dumps(result) # Change here.