bonfy / problems

record problems I have met and solved
2 stars 1 forks source link

Flask + pymongo when jsonify: TypeError: ObjectId('573194e52119751624ae1e33') is not JSON serializable #1

Open bonfy opened 7 years ago

bonfy commented 7 years ago

原因如出错里面报错的提示: 是 ObjectId() 与 flask的jsonify 不兼容,jsonify无法解析这个

@app.route('/post/<int:page>')
def post(page):
    # db = pymongo.MongoClient("localhost", 27017)
    # g.db = db
    Post = g.db.test.posts # 注意这个 db.test 一开始漏了检查了半天
    pts = Post.find().skip(page*10).limit(10) # pts type <class 'pymongo.cursor.Cursor'>
    data = [item for item in pts]
    return jsonify(data = data)

报错:

TypeError: ObjectId('573194e52119751624ae1e33') is not JSON serializable

补充个知识:

bson ObjectId 在 Pymongo文档中的解释

objectid – Tools for working with MongoDB ObjectIds Tools for working with MongoDB ObjectIds.

class bson.objectid.ObjectId(oid=None) Initialize a new ObjectId.

An ObjectId is a 12-byte unique identifier consisting of:

bonfy commented 7 years ago

这个问题肯定能解决,大不了手动将 item 里面的 _id 这个项去掉,只是比较麻烦,或者写个通用的Util,只是想看看别人的解决办法,找出较优解法

先上自己的解法:

def serial(dct):
    for k in dct:
        if isinstance(dct[k], ObjectId):
            dct[k] = 'objectid'   # 试过 dct[k] = str(dct[k]) 不行
    return dct

@app.route('/post/<int:page>')
def post(page):
    Post = g.db.test.posts # 注意这个 db.test 一开始漏了检查了半天
    pts = Post.find().skip(page*10).limit(10)
    data = [serial(item) for item in pts]
    return jsonify(data = data)
bonfy commented 7 years ago

http://stackoverflow.com/questions/11280382/python-mongodb-pymongo-json-encoding-and-decoding

import json
from bson import json_util

@app.route('/post/<int:page>')
def post(page):
    Post = g.db.test.posts # 注意这个 db.test 一开始漏了检查了半天
    pts = Post.find().skip(page*10).limit(10)
    data = [json.dumps(item, default=json_util.default) for item in pts]
    return jsonify(data = data)

不报错,得到结果:

{
data: [
"{"id": "116291239", "type": "text", "content": "\u6211\u6709\u4e2a\u7fa4 \u7fa4\u91cc\u4e00\u4e2a\u59b9\u5b50\u548c\u5979\u8868\u5f1f\u90fd\u5728\u7fa4\u91cc \u6628\u5929\u6211\u4eec\u5728\u804a\u4e00\u4e9b\u654f\u611f\u8bdd\u9898 \u90a3\u59b9\u5b50\u89c9\u5f97\u5979\u8868\u5f1f\u770b\u5230\u4e0d\u597d\u5c31\u6253\u7b97\u8ba9\u6211\u628a\u5979\u8868\u5f1f\u8e22\u51fa\u7fa4 \u7ed3\u679c\u2026\u2026\u79c1\u804a\u7ed9\u6211\u7684\u4fe1\u606f\u4e0d\u77e5\u9053\u600e\u4e48\u6ef4\u76f4\u63a5\u53d1\u7ed9\u5979\u8868\u5f1f\u4e86 \u5185\u5bb9\u662f\uff1a\u5f3a\u54e5\uff01\u628a\u6211\u8868\u5f1f\u8e22\u51fa\u7fa4 \u4ed6\u5728\u7fa4\u91cc\u6211\u597d\u5c34\u5c2c", "_id": {"$oid": "573194e52119751624ae1e33"}}",
"{"id": "116291571", "type": "text", "content": "\u5e73\u65f6\u5de5\u4f5c\u8ba4\u771f\uff0c\u5374\u603b\u662f\u88ab\u5973\u4e0a\u53f8\u7a7f \u5c0f \u978b\uff0c\u5fcd\u65e0\u53ef\u5fcd\u6253\u62a5\u544a\u8f9e\u804c\uff0c\u53c8\u88ab\u5979\u51b7\u5632\u70ed\u8bbd\u595a\u843d\uff0c\u6c14\u6025\u4e86\uff0c\u5f53\u540c\u4e8b\u9762\uff0c\u628a\u5979\u548c\u5c0f\u7537\u4eba\u7ea6 \u4f1a\u7684\u662f\u6296 \u843d\u51fa\u6765\uff0c\u5973\u4e0a\u53f8\u521a\u8981\u53d1\u706b\uff0c\u53f8\u673a\u5c0f\u674e\u6012\u4e86\uff0c\u8138\u618b\u901a\u7ea2\uff0c\u7528\u624b\u6307\u7740\u5973\u4e0a\u53f8\u201c\u4f60\u4e0d\u662f\u53ea\u7231\u6211\u5417\uff01\u4e3a\u4ec0\u4e48\uff1f\u4e3a\u4ec0\u4e48\u8fd8\u8981\u627e\u522b\u4eba\uff01\uff01\u201d \u54ed\u7684\u75db\u4e0d\u6b32\u751f\uff0c\u6495\u5fc3\u88c2\u80ba\uff01\uff01\uff01 \u5c3c \u739b\uff01\u4ec0\u4e48\u60c5\u51b5\uff01\u6211\u4eec\u5f7b\u5e95\u5168\u61f5 \u903c\u4e86\uff01\u5973\u4e0a\u53f8\u8138\u90fd\u7eff\u4e86\uff01\uff01", "_id": {"$oid": "573194e52119751624ae1e34"}}",
"{"id": "116291168", "type": "text", "content": "\u6628\u5929\u770b\u4e86\u4e00\u6761\u7cd7\u4e8b\uff0c\u51c6\u5907\u4eca\u5929\u7ed9\u529e\u516c\u5ba4\u7684\u4eba\u8bb2\u8bb2\uff0c\u8c01\u77e5\u9053\u7761\u4e86\u4e00\u89c9\u5fd8\u8bb0\u4e86\uff0c\u5c31\u8bf4\uff0c\u5b8c\u4e86\uff0c\u6211\u5c0f\u5e74\u75f4\u5446\u4e86\uff0c\u7136\u540e\uff0c\u7136\u540e\u7ec4\u957f\u8bf4\uff0c\u4f60\u90a3\u4e2a\u4e0d\u662f\u5c0f\u5e74\u75f4\u5446\uff0c\u53eb\u5f31\u667a\uff01\u989d\uff0c\u7ec4\u957f\uff0c\u6211\u6700\u8fd1\u6ca1\u6709\u72af\u9519\uff0c\u5bf9\u5427\uff1f", "_id": {"$oid": "573194e52119751624ae1e35"}}",
"{"id": "116291348", "type": "text", "content": "\u6709\u65f6\u5019\u51fa\u95e8\u624e\u4e2a\u4e38\u5b50\u59342\u4e2a\u5c0f\u65f6\u90fd\u624e\u4e0d\u597d\uff0c\u7136\u800c\u5f53\u6211\u53bb\u6d17\u6fa1\u65f6\u968f\u624b\u624e\u4e2a\u4e38\u5b50\u5934\u90fd\u89c9\u5f97\u7f8e\u5230\u7206\u70b8\uff01", "_id": {"$oid": "573194e52119751624ae1e36"}}",
"{"id": "116291645", "type": "text", "content": "\u4eca\u5929\u6574\u7406\u4e1c\u897f\u53d1\u73b0\u8fd8\u6709\u76d2\u5957\u5957\u6ca1\u6709\u7528\u5b8c\uff0c\u60f3\u60f3\u5973\u670b\u53cb\u90fd\u5206\u624b\u597d\u51e0\u4e2a\u6708\u4e86\uff0c\u53ef\u8fd8\u662f\u6ca1\u820d\u5f97\u6254\u3002\u5957\u5957\u4fdd\u8d28\u671f\u786e\u5b9e\u6bd4\u7231\u60c5\u957f\u3002", "_id": {"$oid": "573194e52119751624ae1e37"}}",
"{"id": "116291215", "type": "text", "content": "\u6628\u665a\u51e0\u4e2a\u4eba\u4e00\u8d77\u53bb\u540c\u4e8b\u5bb6\u5403\u996d\u3002\u9152\u8db3\u996d\u9971\u5973a\u548c\u5973b\u716e\u9762\u6761\u3002a\u5973\u5728\u7092\u897f\u7ea2\u67ff\u9e21\u86cb\u3002\u6c34\u5f00\u4e86\u5c31\u5bf9b\u5973\u8bf4\uff1a\u4f60\u4e0b\u9762\u600e\u4e48\u6837\uff1f\uff1f\uff1fb\u5973\u5a07\u7f9e\u9053\uff1a\u6bd4\u539f\u6765\u53c8\u9ed1\u4e86\u70b9\u3002\u3002\u3002\u6211\u4eec\u5728\u9910\u5385\u54c8\u54c8\u5927\u7b11\u3002\u9ad8\u6f6e\u662f\u540c\u4e8bc\u7b11\u55b7\u4e86\uff0c\u9f3b\u5b50\u91cc\u8fde\u7740\u55b7\u51fa\u6765\u4e86\u4e24\u4e2a\u9ec4\u8c46\u3002\u3002\u3002\u3002\u3002\u3002", "_id": {"$oid": "573194e52119751624ae1e38"}}",
"{"id": "116291258", "type": "text", "content": "\u559d\u6655\u4e86\uff0c\u8eba\u5728\u5ba2\u5385\uff0c\u95ed\u7740\u773c\u542c\u7740\u5468\u6770\u4f26\u7684\u300a\u5b89\u9759\u300b\uff0c\u6709\u70b9\u4f24\u611f\u3002\u6211\u5988\u4e0d\u77e5\u9053\u4ece\u54ea\u5192\u4e86\u51fa\u6765\uff0c\u4e00\u8fb9\u7ed9\u6211\u5012\u6c34\u4e00\u8fb9\u6570\u843d\u6211\u518d\u4e0d\u627e\u4e2a\u5bf9\u8c61\u4ee5\u540e\u8fde\u5012\u6c34\u7684\u4eba\u90fd\u6ca1\uff0c\u6211\u53cd\u9a73\u5979\u8bf4\u627e\u5bf9\u8c61\u53c8\u4e0d\u662f\u4e3a\u4e86\u5012\u6c34\uff0c\u6211\u5988\u6c89\u9ed8\u4e86\u4e00\u4f1a\uff0c\u8bf4\u90a3\u6700\u8d77\u7801\u6709\u4e2a\u4eba\u53ef\u4ee5\u966a\u4f60\u4e00\u8d77\u542c\u6b4c\u5427\uff0c\u6211\u521a\u624d\u770b\u4f60\u4e00\u4e2a\u4eba\u8eba\u5728\u90a3\uff0c\u89c9\u5f97\u4f60\u597d\u5b64\u5355\u3002", "_id": {"$oid": "573194e52119751624ae1e39"}}",
"{"id": "116291196", "type": "text", "content": "\u5750\u7740\u5927\u4fbf\u4e0d\u4e60\u60ef\uff0c\u53ea\u597d\u8e72\u5728\u9a6c\u6876\u4e0a\uff0c\u6bcf\u6b21\u5b8c\u4e8b\u4e86\u628a\u9a6c\u6876\u8fb9\u4e0a\u7684\u811a\u5370\u64e6\u7684\u5e72\u5e72\u51c0\u51c0\uff0c\u518d\u628a\u5750\u4fbf\u76d6\u76d6\u4e0a\uff0c\u6015\u88ab\u8001\u5a46\u53d1\u73b0\uff0c5\u5e74\u4e86\u5979\u8fd8\u6ca1\u53d1\u73b0\u6211\u662f\u8e72\u7740\u62c9\u5c4e\u7684\uff0c\u5fc3\u91cc\u6709\u79cd\u6210\u5c31\u611f\uff0c\u4e5f\u6709\u70b9\u5bb3\u6015\uff0c\u603b\u6015\u6709\u4e00\u5929\u88ab\u53d1\u73b0\uff0c\u98a0\u8986\u6211\u5728\u4ed6\u5fc3\u4e2d\u7684\u5f62\u8c61\uff0c\u5fc3\u771f\u7684\u597d\u7d2f", "_id": {"$oid": "573194e52119751624ae1e3a"}}",
"{"id": "116291572", "type": "text", "content": "\u56db\u5c81\u513f\u5b50\u65e9\u4e0a\u53bb\u5e7c\u513f\u56ed\u4e4b\u524d\u8d81\u516c\u516c\u4e0d\u6ce8\u610f\u5077\u8d70\u4e86\u4ed6\u5e73\u65f6\u628a\u73a9\u7684\u6838\u6843~ \u7531\u4e8e\u5e7c\u513f\u56ed\u4e0d\u53ef\u4ee5\u5e26\u5403\u7684\uff0c\u8001\u5e08\u7838\u5f00\u6838\u6843\u903c\u7740\u513f\u5b50\u5403\u5b8c\u4e86\u2026\u2026", "_id": {"$oid": "573194e52119751624ae1e3b"}}",
"{"id": "116291662", "type": "text", "content": "\u4eca\u5929\u7684\u8bfe\u5c31\u4e0a\u5230\u8fd9\u91cc\uff0c\u540c\u5b66\u4eec\u8981\u73cd\u60dc\u554a\u554a\u554a\u3002", "_id": {"$oid": "573194e52119751624ae1e3c"}}"
]
}

里面全变成 string 了, 不是很理想的结果

annilq commented 6 years ago

你好,请问这个序列号的问题你后来有好的解决方式吗? 我目前是这样解决的,首先引入了 bson.json模块,然后自定义了个flask 的Response对象返回的,没有用到jsonify

from bson.json_util import dumps
#省略...
resp = app.make_response(dumps({ "code": 0,"data":user,"message": "注册成功" }))
resp.headers['Content-Type'] = 'application/json'
return resp
AaronConlon commented 5 years ago

我是查找到数据之后,主动pop掉_id处理,不知道各位怎么看.

AlllenShen commented 5 years ago

@annilq 直接把jsonify的代码copy过来就行了,都不用改,功能是完全的。我在utils里面copy了一份jsonify的代码,用的时候从自己的utils导入就行。

from bson.json_util import dumps

def jsonify(*args, **kwargs):
    """Copy了jsonify的原始实现,改为使用bson提供的dumps方法"""
    indent = None
    separators = (',', ':')

    if current_app.config['JSONIFY_PRETTYPRINT_REGULAR'] or current_app.debug:
        indent = 2
        separators = (', ', ': ')

    if args and kwargs:
        raise TypeError('jsonify() behavior undefined when passed both args and kwargs')
    elif len(args) == 1:  # single args are passed directly to dumps()
        data = args[0]
    else:
        data = args or kwargs

    return current_app.response_class(
        dumps(data, indent=indent, separators=separators) + '\n',
        mimetype=current_app.config['JSONIFY_MIMETYPE']
    )

objectID会被序列化为{'$oid': xxx}的dict,接收方注意就行了

annilq commented 5 years ago

@AlllenShen thanks,我后来还是乖乖的做前端了,学不动Python了(┬_┬)

toniree commented 4 years ago

@bonfy Thank you for the elegant solution! Defeat coronavirus!