Pregunta

Creo que me estoy perdiendo algo pequeño aquí. Estoy probando Python Framework Flask y Flask-Mongoalchemy, y quiero convertir una entidad en la salida de JSON. Aquí está mi código (abstracto):

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)

Sin la línea json.dumps, la declaración de impresión solicita el resultado correcto. Pero solo si ejecuto los json.dumps en el resultado, produce:

TypeError: ObjectId ('...') no es JSON Serializable

¿Qué me estoy perdiendo?

¿Fue útil?

Solución

El resultado es un documento de Mongo de algún tipo que contiene contenido de tipo ObjectId, que tendrá que decirle a JSON cómo deserializar. Tendrá el mismo problema con otros tipos específicos de Mongo, como referenceField (), increddedDocumentField (), etc. Debe escribir una función de deserialización que puede pasar a JSON. Lo que uso es:

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

Luego procesaría el resultado como:

return json.dumps(result, default=encode_model)

o algo a este efecto.

Otros consejos

También puedes usar el query.raw_output() Método para que esa instancia de consulta devuelva los diccionarios de pitón sin procesar en lugar de un objeto Python. Con un diccionario se hace fácil de codificar a JSON usando json.dumps():

import json
q=db.query(MyObject)
q.raw_output()
json.dumps(q.first())

Referencia http://www.mongoalchemy.org/api/expressions/query.html#mongoalchemy.query.query.raw_output

Combinando las dos respuestas anteriores, debería poder hacer algo como esto:

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.
Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top