对象无法进行JSON序列化

22

我在使用Mongodb和Python(Flask)时遇到了一些问题。

我有一个api.py文件,我希望所有请求和响应都是JSON格式的,因此我按照以下方式实现。

#
# Imports
#

from datetime import datetime
from flask import Flask
from flask import g
from flask import jsonify
from flask import json
from flask import request
from flask import url_for
from flask import redirect
from flask import render_template
from flask import make_response
import pymongo
from pymongo import Connection
from bson import BSON
from bson import json_util

#
# App Create
#

app = Flask(__name__)
app.config.from_object(__name__)

#
# Database
#

# connect
connection = Connection()
db = connection['storage']
units = db['storage']


#
# Request Mixins
#

@app.before_request
def before_request():
    #before
    return

@app.teardown_request
def teardown_request(exception):
    #after
    return


#
# Functions
#

def isInt(n):
    try:
        num = int(n)
        return True
    except ValueError:
        return False

def isFloat(n):
    try:
        num = float(n)
        return True
    except ValueError:
        return False

def jd(obj):
    return json.dumps(obj, default=json_util.default)

def jl(obj):
    return json.loads(obj, object_hook=json_util.object_hook)

#
# Response
#

def response(data={}, code=200):
    resp = {
        "code" : code,
        "data" : data
    }
    response = make_response(jd(resp))
    response.headers['Status Code'] = resp['code']
    response.headers['Content-Type'] = "application/json"
    return response


#
# REST API calls
#

# index
@app.route('/')
def index():
    return response()

# search
@app.route('/search', methods=['POST'])
def search(): 
    return response()

# add
@app.route('/add', methods=['POST'])
def add():
    unit = request.json
    _id = units.save(unit)
    return response(_id)

# get
@app.route('/show', methods=['GET'])
def show():
    import pdb; pdb.set_trace();
    return response(db.units.find())

#
# Error handing
#

@app.errorhandler(404)
def page_not_found(error):
    return response({},404)


#
# Run it!
#

if __name__ == '__main__':
    app.debug = True
    app.run()
这里的问题是JSON编码来自和返回到Mongo的数据。通过将request.json作为save字典的传递,我似乎已经能够“入侵”添加路由,所以这很好...但问题在于/show。这段代码不起作用...当我进行一些日志记录时,会得到:
TypeError: <pymongo.cursor.Cursor object at 0x109bda150> is not JSON serializable
任何想法?我也欢迎对其余代码的任何建议,但JSON让我很头疼。
提前感谢!

我只想将导致我出现这个问题的错误添加到这个问题和解决方案中:TypeError: unhashable type: 'dict' - Mike Shultz
7个回答

36

虽然@ErenGüven向你展示了一个解决这个json序列化问题的不错的手动方法,但是pymongo带有一个实用工具可以为您完成此操作。我在我的django mongodb项目中使用它:

import json
from bson import json_util

json_docs = []
for doc in cursor:
    json_doc = json.dumps(doc, default=json_util.default)
    json_docs.append(json_doc)

或者简单地说:

json_docs = [json.dumps(doc, default=json_util.default) for doc in cursor]

而要从 JSON 中获取它们:

docs = [json.loads(j_doc, object_hook=json_util.object_hook) for j_doc in json_docs]

这些辅助工具告诉json如何处理自定义的mongodb对象。


你可以看到我也在使用这个,我的问题是忘记通过游标进行迭代!不过还是谢谢 :) - willium
from pymongo import json_util已经被弃用,请使用from bson import json_util - super9

22
当你将 db.units.find() 传递给 response时,你传递了一个 pymongo.cursor.Cursor 对象到 json.dumps 函数中,而 json.dumps 不知道如何将其序列化为 JSON。尝试通过迭代光标以获取其结果来获取实际对象:
[doc for doc in db.units.find()]

@YisraelDov - 你的设置和OP一样吗?请注意他正在使用json.dumps(obj, default=json_util.default),而不仅仅是json.dumps(obj) - Sean Vieira
当我执行db.units.find()[:]时,它总是返回一个游标对象。最终我只能通过循环遍历并添加到数组中,然后才能正常工作。 - Yisrael Dov
是的,看起来API已经改变了...我会更新我的答案。 - Sean Vieira

14
import json
from bson import json_util

docs_list  = list(db.units.find())
return json.dumps(docs_list, default=json_util.default)

4
为了将MongoDB文档编码为JSON,我使用类似下面的方法来处理和datetime.datetime类型。
class CustomEncoder(json.JSONEncoder):
    """A C{json.JSONEncoder} subclass to encode documents that have fields of
    type C{bson.objectid.ObjectId}, C{datetime.datetime}
    """
    def default(self, obj):
        if isinstance(obj, bson.objectid.ObjectId):
            return str(obj)
        elif isinstance(obj, datetime.datetime):
            return obj.isoformat()
        return json.JSONEncoder.default(self, obj)

enc = CustomEncoder()
enc.encode(doc)

关于游标,您需要先迭代它并获取文档。


0

如果文档ID没有用处,您可以通过以下方式将其排除:

objects = collection.find({}, {'_id': False})

接下来,只需通过以下方式将其转换为字典列表:

list(objects)

0

如果您正在寻找一种更简单的Python方式来发送响应,则可以创建一个空列表并将其中的值附加到其中,最后以JSON格式返回此列表。

from flask import Flask, jsonify

@app.route('/view', methods=["GET'])
def viewFun():
   data = units.find()
   s = []
   for i in data:
      s.append(str(i))
   return jsonify(s)

可能不是完美的,但它能工作!


0
简短回答:它是一个游标对象。遍历它,你会得到Python字典。再次进行序列化时,执行以下操作:
import json 
from bson import json_util

假设这是我的查询:

details = mongo.db.details.find()
# this is cursor object

#iterate over to get a list of dicts
details_dicts = [doc for doc in details]

#serialize to json string
details_json_string = json.dumps(details_dicts,default=json_util.default)

如果您希望返回上述内容,它将只是一个字符串。请执行以下操作,以便将其作为可用的字典或JSON返回。
return json.loads(details_json_string)
#return jsonified version rather than string without  unwanted "\" in earlier json string!

希望这能帮到你!编程愉快!
是的,普通的jsonify可以工作,如果你的查询不包含一些复杂的字段,比如Objectid等!

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接