我使用Python在文档上查询聚合函数后从MongoDB返回,它返回有效响应,我可以打印但不能返回它。
错误:
TypeError: ObjectId('51948e86c25f4b1d1c0d303c') is not JSON serializable
打印:
{'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}
但是当我试图回来时:
TypeError: ObjectId('51948e86c25f4b1d1c0d303c') is not JSON serializable
这是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连接良好,集合也在那里,我得到了有效的预期结果,但是当我尝试返回时,它给了我Json错误。知道如何将响应转换回JSON。感谢
答案 0 :(得分:102)
答案 1 :(得分:88)
您应该定义自己的JSONEncoder
并使用它:
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)
也可以通过以下方式使用它。
json.encode(analytics, cls=JSONEncoder)
答案 2 :(得分:29)
>>> 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"}}]'
来自json_util的实际示例。
与Flask的jsonify不同,“dumps”将返回一个字符串,因此它不能用作Flask的jsonify的1:1替换。
但是this question表明我们可以使用json_util.dumps()进行序列化,使用json.loads()转换回dict,最后调用Flask的jsonify。
示例(源自上一个问题的答案):
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
此解决方案将ObjectId和其他(即Binary,Code等)转换为等效字符串,例如“$ oid”。
JSON输出如下所示:
{
"_id": {
"$oid": "abc123"
}
}
答案 3 :(得分:17)
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)
这是将BSON转换为JSON对象的示例示例。你可以试试这个。
答案 4 :(得分:16)
作为快速替换,您可以将{'owner': objectid}
更改为{'owner': str(objectid)}
。
但是定义自己的JSONEncoder
是一个更好的解决方案,它取决于您的要求。
答案 5 :(得分:5)
这就是我最近修正错误的方法
@app.route('/')
def home():
docs = []
for doc in db.person.find():
doc.pop('_id')
docs.append(doc)
return jsonify(docs)
答案 6 :(得分:3)
我知道我发帖迟到但认为这对至少有几个人有帮助!
蒂姆和defuz提到的两个例子(最高投票)都很好。但是,有时会出现微小的差异。
Pymongo提供了json_util - 您可以使用它来代替处理BSON类型
输出:{ “_ID”: { “$ oid”:“abc123” } }
输出:{ “_id”:“abc123” }
尽管第一种方法看起来很简单,但这种方法都需要很少的努力。
答案 7 :(得分:3)
我认为在此处发布信息对于将Flask
与pymongo
结合使用的人可能有用。这是我当前的“最佳做法”设置,用于允许flask封送pymongo bson数据类型。
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
为什么这样做而不是为BSON或mongod extended JSON服务?
我认为提供mongo特殊JSON给客户端应用程序带来了负担。大多数客户端应用程序都不会以任何复杂的方式使用mongo对象。如果我提供扩展的json,现在必须在服务器端和客户端使用它。 ObjectId
和Timestamp
更容易作为字符串使用,这使所有这种mongo编组的疯狂都隔离在服务器上。
{
"_id": "5b6b6959828619572d48a9da",
"created_at": "2018-08-08T22:06:17Z"
}
对于大多数 应用程序来说,我认为使用它的繁琐程度要比
{
"_id": {"$oid": "5b6b6959828619572d48a9da"},
"created_at": {"$date": 1533837843000}
}
答案 8 :(得分:2)
Flask的jsonify提供了JSON Security中描述的安全性增强功能。如果自定义编码器与Flask一起使用,最好考虑一下 JSON Security
中讨论的要点答案 9 :(得分:1)
你可以试试:
objectid = str(ObjectId("51948e86c25f4b1d1c0d303c"))
答案 10 :(得分:1)
如果您不需要记录的_id,我建议在查询数据库时将其取消设置,这将使您可以直接打印返回的记录,例如
要在查询时取消设置_id,然后在循环中打印数据,请编写如下内容
records = mycollection.find(query, {'_id': 0}) #second argument {'_id':0} unsets the id from the query
for record in records:
print(record)
答案 11 :(得分:0)
解决方案:mongoengine +棉花糖
如果您使用mongoengine
和marshamallow
,则此解决方案可能适用于您。
基本上,我从棉花糖中导入了String
字段,然后将默认的Schema id
覆盖为String
编码。
from marshmallow import Schema
from marshmallow.fields import String
class FrontendUserSchema(Schema):
id = String()
class Meta:
fields = ("id", "email")
答案 12 :(得分:0)
就我而言,我需要这样的东西:
class JsonEncoder():
def encode(self, o):
if '_id' in o:
o['_id'] = str(o['_id'])
return o
答案 13 :(得分:0)
使用json.dumps
时,大多数收到“无法JSON序列化”错误的用户只需指定 default=str
。例如:
json.dumps(my_obj, default=str)
这将强制转换为str
,从而防止出现错误。当然,然后查看生成的输出以确认它就是您所需要的。
答案 14 :(得分:0)
我想提供一个附加的解决方案,以改善公认的答案。我以前在另一个线程here中提供了答案。
from flask import Flask
from flask.json import JSONEncoder
from bson import json_util
from . import resources
# define a custom encoder point to the json_util provided by pymongo (or its dependency bson)
class CustomJSONEncoder(JSONEncoder):
def default(self, obj): return json_util.default(obj)
application = Flask(__name__)
application.json_encoder = CustomJSONEncoder
if __name__ == "__main__":
application.run()
答案 15 :(得分:0)
from bson.objectid import ObjectId
from core.services.db_connection import DbConnectionService
class DbExecutionService:
def __init__(self):
self.db = DbConnectionService()
def list(self, collection, search):
session = self.db.create_connection(collection)
return list(map(lambda row: {i: str(row[i]) if isinstance(row[i], ObjectId) else row[i] for i in row}, session.find(search))
答案 16 :(得分:0)
如果您不希望_id
作为响应,则可以重构代码,如下所示:
jsonResponse = getResponse(mock_data)
del jsonResponse['_id'] # removes '_id' from the final response
return jsonResponse
这将消除TypeError: ObjectId('') is not JSON serializable
错误。
答案 17 :(得分:0)
对于那些需要通过Flask通过Jsonify返回数据的人:
cursor = db.collection.find()
data = []
for doc in cursor:
doc['_id'] = str(doc['_id']) # This does the trick!
data.append(doc)
return jsonify(data)