Glorithm Glorithm - 11 months ago 139
Python Question

MongoAlchemy Document Encode to JSON via Flask-MongoAlchemy

I think I am missing something small here. I am testing out Python framework Flask and Flask-MongoAlchemy, and want to convert an entity into JSON output. Here is my code (abstracted):

from flask import Flask
from flaskext.mongoalchemy import MongoAlchemy

from bson.objectid import ObjectId

#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:
print result
return json.dumps(result)

Without the json.dumps line, the print statement prompt the right result. But only if I run the json.dumps on result, it yields:

TypeError: ObjectId('...') is not JSON serializable

What am I missing?

Answer Source

The result is a mongo document of some sort that contains ObjectId-type content, which you'll have to tell json how to deserialize. You'll have the same problem with other mongo-specific types, such as ReferenceField(), EmbeddedDocumentField(), etc. You have to write a deserialization function that you can pass to json. What I use is:

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)
                    # Unlikely that we'll hit this since ObjectId is always NULL key
                    out[k] = str(v)
                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)
        raise TypeError, "Could not JSON-encode type '%s': %s" % (type(obj), str(obj))
    return out

Then you'd process the result as:

return json.dumps(result, default=encode_model)

or something to this effect.