TypeError:类型为ObjectId的对象不可JSON序列化 [英] TypeError: Object of type ObjectId is not JSON serializable

查看:343
本文介绍了TypeError:类型为ObjectId的对象不可JSON序列化的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我一直在开发一个简单的app.py,下面是代码:

from flask import Flask, request, jsonify, make_response
from pymongo import MongoClient
from bson import ObjectId

app = Flask(__name__)

client = MongoClient("mongodb://127.0.0.1:27017")
db = client.bizDB
businesses = db.biz


@app.route("/api/v1.0/businesses", methods=["GET"])
def show_all_businesses():
    page_num, page_size = 1, 10
    if request.args.get("pn"):
        page_num = int(request.args.get("pn"))
    if request.args.get("ps"):
        page_size = int(request.args.get("ps"))
    page_start = page_size * (page_num - 1)

    data_to_return = []
    for business in businesses.find().skip(page_start).limit(page_size):
        business["_id"] = str(business["_id"])
        for review in business["reviews"]:
            review["_id"] = str(review["_id"])
        data_to_return.append(business)

    return make_response(jsonify(data_to_return), 200)


@app.route("/api/v1.0/businesses/<string:id>", methods=["GET"])
def show_one_business(id):
    business = businesses.find_one({"_id": ObjectId(id)})
    if business is not None:
        business["_id"] = str(business["_id"])
        for review in business["reviews"]:
            review["_id"] = str(review["_id"])
        return make_response(jsonify(business), 200)
    else:
        return make_response(jsonify({"error": "Invalid Business ID"}), 404)


@app.route("/api/v1.0/businesses", methods=["POST"])
def add_business():
    if "name" in request.form and "town" in request.form and "rating" in request.form:
        new_business = {"name": request.form["name"],
                        "town": request.form["town"],
                        "rating": request.form["rating"],
                        "reviews": {}
                        }
        new_business_id = businesses.insert_one(new_business)
        new_business_link = "http://localhost:5000/api/v1.0/businesses/" + \
            str(new_business_id.inserted_id)

        return make_response(jsonify({"url": new_business_link}), 201)
    else:
        return make_response(jsonify({"error": "Missing form data"}), 404)


@app.route("/api/v1.0/businesses/<string:id>", methods=["PUT"])
def edit_business(id):
    if "name" in request.form and "town" in request.form and "rating" in request.form:
        result = businesses.update_one(
            {"_id": ObjectId(id)},
            {
                "$set": {
                    "name": request.form["name"],
                    "town": request.form["town"],
                    "rating": request.form["rating"]
                }
            }
        )
        if result.matched_count == 1:
            edited_business_link = "http://localhost:5000/api/v1.0/businesses/" + id
            return make_response(jsonify({"url": edited_business_link}), 200)
        else:
            return make_response(jsonify({"error": "Invalid business ID"}), 404)
    else:
        return make_response(jsonify({"error": "Missing form data"}), 404)


@app.route("/api/v1.0/businesses/<string:id>", methods=["DELETE"])
def delete_business(id):
    result = businesses.delete_one({"_id": ObjectId(id)})
    if result.deleted_count == 1:
        return make_response(jsonify({}), 200)
    else:
        return make_response(jsonify({"error": "Invalid business ID"}), 404)


@app.route("/api/v1.0/businesses/<string:id>/reviews", methods=["POST"])
def add_new_review(id):
    new_review = {
        "_id": ObjectId(),
        "username": request.form["username"],
        "comment": request.form["comment"],
        "stars": request.form["stars"]
    }
    businesses.update_one({"_id": ObjectId(id)}, {
                          "$push": {"reviews ": new_review}})
    new_review_link = "http://localhost:5000/api/v1.0/businesses/" + \
        id + "/reviews/" + str(new_review["_id"])
    return make_response(jsonify({"url": new_review_link}), 201)


if __name__ == "__main__":
    app.run(debug=True)

Eveyrthing一直运行良好,直到在VS Code中我意外地在文件上单击了格式文档,Postman先前从mongoDB返回了我的结果,但是现在我收到TypeError错误:ObjectId类型的对象不可JSON序列化. >

我不知道如何解决此问题或问题所在.

解决方案

您的根本原因是,在您返回的响应中,对象包含类型为ObjectId的字段,该字段始终为_id字段.

要全面修复它,请使用我们自己的自定义JSON编码器,该编码器会自动将ObjectId转换为字符串.有了这些代码后,您的代码就会变得很整洁,因为您可以删除所有需要手动进行转换的代码.

编码器示例如下:

class MyEncoder(json.JSONEncoder):

    def default(self, obj):
        if isinstance(obj, ObjectId):
            return str(obj)
        return super(MyEncoder, self).default(obj) 

然后让flask知道如何将其用于:

app.json_encoder = MyEncoder

将所有内容放在一起作为一个最小的示例是:

from pymongo import MongoClient
from flask import Flask, jsonify
from bson.json_util import ObjectId
import json


class MyEncoder(json.JSONEncoder):

    def default(self, obj):
        if isinstance(obj, ObjectId):
            return str(obj)
        return super(MyEncoder, self).default(obj)


db = MongoClient()['mydatabase']
app = Flask(__name__)
app.json_encoder = MyEncoder

with app.test_request_context():
    db.mycollection.insert_one({'a': 1})
    doc = db.mycollection.find_one({'a': 1})

    print(jsonify(doc).response)

打印:

[b'{"_id":"5ff055dffab2033526bd0b63","a":1}\n']

注意:如果您注释掉app.json_encoder = MyEncoder行,则会重现您的错误:

    return _json.JSONEncoder.default(self, o)
  File "C:\Python38\lib\json\encoder.py", line 179, in default
    raise TypeError(f'Object of type {o.__class__.__name__} '
TypeError: Object of type ObjectId is not JSON serializable

I have been developing a simple app.py, here is the code:

from flask import Flask, request, jsonify, make_response
from pymongo import MongoClient
from bson import ObjectId

app = Flask(__name__)

client = MongoClient("mongodb://127.0.0.1:27017")
db = client.bizDB
businesses = db.biz


@app.route("/api/v1.0/businesses", methods=["GET"])
def show_all_businesses():
    page_num, page_size = 1, 10
    if request.args.get("pn"):
        page_num = int(request.args.get("pn"))
    if request.args.get("ps"):
        page_size = int(request.args.get("ps"))
    page_start = page_size * (page_num - 1)

    data_to_return = []
    for business in businesses.find().skip(page_start).limit(page_size):
        business["_id"] = str(business["_id"])
        for review in business["reviews"]:
            review["_id"] = str(review["_id"])
        data_to_return.append(business)

    return make_response(jsonify(data_to_return), 200)


@app.route("/api/v1.0/businesses/<string:id>", methods=["GET"])
def show_one_business(id):
    business = businesses.find_one({"_id": ObjectId(id)})
    if business is not None:
        business["_id"] = str(business["_id"])
        for review in business["reviews"]:
            review["_id"] = str(review["_id"])
        return make_response(jsonify(business), 200)
    else:
        return make_response(jsonify({"error": "Invalid Business ID"}), 404)


@app.route("/api/v1.0/businesses", methods=["POST"])
def add_business():
    if "name" in request.form and "town" in request.form and "rating" in request.form:
        new_business = {"name": request.form["name"],
                        "town": request.form["town"],
                        "rating": request.form["rating"],
                        "reviews": {}
                        }
        new_business_id = businesses.insert_one(new_business)
        new_business_link = "http://localhost:5000/api/v1.0/businesses/" + \
            str(new_business_id.inserted_id)

        return make_response(jsonify({"url": new_business_link}), 201)
    else:
        return make_response(jsonify({"error": "Missing form data"}), 404)


@app.route("/api/v1.0/businesses/<string:id>", methods=["PUT"])
def edit_business(id):
    if "name" in request.form and "town" in request.form and "rating" in request.form:
        result = businesses.update_one(
            {"_id": ObjectId(id)},
            {
                "$set": {
                    "name": request.form["name"],
                    "town": request.form["town"],
                    "rating": request.form["rating"]
                }
            }
        )
        if result.matched_count == 1:
            edited_business_link = "http://localhost:5000/api/v1.0/businesses/" + id
            return make_response(jsonify({"url": edited_business_link}), 200)
        else:
            return make_response(jsonify({"error": "Invalid business ID"}), 404)
    else:
        return make_response(jsonify({"error": "Missing form data"}), 404)


@app.route("/api/v1.0/businesses/<string:id>", methods=["DELETE"])
def delete_business(id):
    result = businesses.delete_one({"_id": ObjectId(id)})
    if result.deleted_count == 1:
        return make_response(jsonify({}), 200)
    else:
        return make_response(jsonify({"error": "Invalid business ID"}), 404)


@app.route("/api/v1.0/businesses/<string:id>/reviews", methods=["POST"])
def add_new_review(id):
    new_review = {
        "_id": ObjectId(),
        "username": request.form["username"],
        "comment": request.form["comment"],
        "stars": request.form["stars"]
    }
    businesses.update_one({"_id": ObjectId(id)}, {
                          "$push": {"reviews ": new_review}})
    new_review_link = "http://localhost:5000/api/v1.0/businesses/" + \
        id + "/reviews/" + str(new_review["_id"])
    return make_response(jsonify({"url": new_review_link}), 201)


if __name__ == "__main__":
    app.run(debug=True)

Eveyrthing has been working fine up until in VS Code I clicked format document by accident on the file, Postman previously returned my results from mongoDB but now I receive an error of TypeError: Object of type ObjectId is not JSON serializable.

I don't know how to fix this issue or what the problem is.

解决方案

Your root cause is that in the responses you are returning, the object contains a field of type ObjectId which is invariably the _id field.

To fix it across the board, use our own custom JSON encoder that automatically converts the ObjectIds to strings. Once you have this your code tidies up a lot because you can remove all the code where you are manually needing to do the conversion.

An example encoder looks like:

class MyEncoder(json.JSONEncoder):

    def default(self, obj):
        if isinstance(obj, ObjectId):
            return str(obj)
        return super(MyEncoder, self).default(obj) 

And you let flask know to use it with:

app.json_encoder = MyEncoder

Putting it all together a minimal example would be:

from pymongo import MongoClient
from flask import Flask, jsonify
from bson.json_util import ObjectId
import json


class MyEncoder(json.JSONEncoder):

    def default(self, obj):
        if isinstance(obj, ObjectId):
            return str(obj)
        return super(MyEncoder, self).default(obj)


db = MongoClient()['mydatabase']
app = Flask(__name__)
app.json_encoder = MyEncoder

with app.test_request_context():
    db.mycollection.insert_one({'a': 1})
    doc = db.mycollection.find_one({'a': 1})

    print(jsonify(doc).response)

prints:

[b'{"_id":"5ff055dffab2033526bd0b63","a":1}\n']

Note: If you comment out the line app.json_encoder = MyEncoder you reproduce your error:

    return _json.JSONEncoder.default(self, o)
  File "C:\Python38\lib\json\encoder.py", line 179, in default
    raise TypeError(f'Object of type {o.__class__.__name__} '
TypeError: Object of type ObjectId is not JSON serializable

这篇关于TypeError:类型为ObjectId的对象不可JSON序列化的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
相关文章
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆