Python 类型错误:ObjectId('') 不是 JSON 可序列化的

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/16586180/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-18 23:05:43  来源:igfitidea点击:

TypeError: ObjectId('') is not JSON serializable

pythonjsonmongodbflask

提问by Irfan

My response back from MongoDB after querying an aggregated function on document using Python, It returns valid response and i can print it but can not return it.

在使用 Python 查询文档上的聚合函数后,我从 MongoDB 返回响应,它返回有效响应,我可以打印它但无法返回它。

Error:

错误:

TypeError: ObjectId('51948e86c25f4b1d1c0d303c') is not JSON serializable

Print:

打印:

{'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}

But When i try to return:

但是当我尝试返回时:

TypeError: ObjectId('51948e86c25f4b1d1c0d303c') is not JSON serializable

It is RESTfull call:

这是 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 is well connected and collection is there too and I got back valid expected result but when i try to return it gives me Json error. Any idea how to convert the response back into JSON. Thanks

db 连接良好,集合也在那里,我得到了有效的预期结果,但是当我尝试返回时,它给了我 Json 错误。知道如何将响应转换回 JSON。谢谢

采纳答案by defuz

You should define you own JSONEncoderand using it:

您应该定义自己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)

It's also possible to use it in the following way.

也可以通过以下方式使用它。

json.encode(analytics, cls=JSONEncoder)

回答by MostafaR

As a quick replacement, you can change {'owner': objectid}to {'owner': str(objectid)}.

作为快速替换,您可以更改{'owner': objectid}{'owner': str(objectid)}.

But defining your own JSONEncoderis a better solution, it depends on your requirements.

但是定义自己JSONEncoder的解决方案是更好的解决方案,这取决于您的要求。

回答by tim

Pymongoprovides json_util- you can use that one instead to handle BSON types

Pymongo提供了json_util- 你可以使用它来处理 BSON 类型

回答by Garren S

>>> 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"}}]'

Actual example from json_util.

来自json_util 的实际示例。

Unlike Flask's jsonify, "dumps" will return a string, so it cannot be used as a 1:1 replacement of Flask's jsonify.

与 Flask 的 jsonify 不同,"dumps" 将返回一个字符串,因此它不能用作 Flask 的 jsonify 的 1:1 替换。

But this questionshows that we can serialize using json_util.dumps(), convert back to dict using json.loads() and finally call Flask's jsonify on it.

但是这个问题表明我们可以使用 json_util.dumps() 进行序列化,使用 json.loads() 转换回 dict 并最终在其上调用 Flask 的 jsonify。

Example (derived from previous question's answer):

示例(源自上一个问题的答案):

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

This solution will convert ObjectId and others (ie Binary, Code, etc) to a string equivalent such as "$oid."

此解决方案会将 ObjectId 和其他(即二进制、代码等)转换为等效的字符串,例如“$oid”。

JSON output would look like this:

JSON 输出如下所示:

{
  "_id": {
    "$oid": "abc123"
  }
}

回答by Anish

Flask's jsonify provides security enhancement as described in JSON Security. If custom encoder is used with Flask, its better to consider the points discussed in the JSON Security

Flask 的 jsonify 提供了安全性增强,如JSON Security 中所述。如果自定义编码器与 Flask 一起使用,最好考虑JSON 安全性中讨论的要点

回答by vinit kantrod

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)

This is the sample example for converting BSON into JSON object. You can try this.

这是将 BSON 转换为 JSON 对象的示例示例。你可以试试这个。

回答by Jcc.Sanabria

This is how I've recently fixed the error

这就是我最近修复错误的方式

    @app.route('/')
    def home():
        docs = []
        for doc in db.person.find():
            doc.pop('_id') 
            docs.append(doc)
        return jsonify(docs)

回答by rohithnama

I know I'm posting late but thought it would help at least a few folks!

我知道我发布晚了,但我认为这至少会帮助一些人!

Both the examples mentioned by tim and defuz(which are top voted) works perfectly fine. However, there is a minute difference which could be significant at times.

tim 和 defuz 提到的两个例子(投票最高)都运行良好。但是,有时可能存在显着的微小差异。

  1. The following method adds one extra field which is redundant and may not be ideal in all the cases
  1. 以下方法添加了一个额外的字段,该字段是多余的,可能并非在所有情况下都理想

Pymongo provides json_util - you can use that one instead to handle BSON types

Pymongo 提供了 json_util - 你可以使用它来处理 BSON 类型

Output: { "_id": { "$oid": "abc123" } }

输出:{ "_id": { "$oid": "abc123" } }

  1. Where as the JsonEncoder class gives the same output in the string format as we need and we need to use json.loads(output) in addition. But it leads to
  1. 因为 JsonEncoder 类以我们需要的字符串格式提供相同的输出,我们还需要使用 json.loads(output) 。但它导致

Output: { "_id": "abc123" }

输出:{“_id”:“abc123”}

Even though, the first method looks simple, both the method need very minimal effort.

尽管第一种方法看起来很简单,但这两种方法都需要很少的努力。

回答by nackjicholson

Posting here as I think it may be useful for people using Flaskwith pymongo. This is my current "best practice" setup for allowing flask to marshall pymongo bson data types.

在这里发布,因为我认为它可能对使用Flaskwith 的人有用pymongo。这是我目前允许烧瓶编组 pymongo bson 数据类型的“最佳实践”设置。

mongoflask.py

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

Why do this instead of serving BSON or mongod extended JSON?

为什么要这样做而不是为 BSON 或mongod 扩展 JSON提供服务?

I think serving mongo special JSON puts a burden on client applications. Most client apps will not care using mongo objects in any complex way. If I serve extended json, now I have to use it server side, and the client side. ObjectIdand Timestampare easier to work with as strings and this keeps all this mongo marshalling madness quarantined to the server.

我认为为 mongo 提供特殊的 JSON 会给客户端应用程序带来负担。大多数客户端应用程序不会关心以任何复杂的方式使用 mongo 对象。如果我提供扩展的 json,现在我必须在服务器端和客户端使用它。ObjectId并且Timestamp更容易作为字符串使用,这将所有这些 mongo 编组疯狂隔离到服务器。

{
  "_id": "5b6b6959828619572d48a9da",
  "created_at": "2018-08-08T22:06:17Z"
}

I think this is less onerous to work with for mostapplications than.

我认为对于大多数应用程序来说,这比使用它更容易。

{
  "_id": {"$oid": "5b6b6959828619572d48a9da"},
  "created_at": {"$date": 1533837843000}
}

回答by Ibrahim Isa

If you will not be needing the _id of the records I will recommend unsetting it when querying the DB which will enable you to print the returned records directly e.g

如果您不需要记录的 _id,我建议您在查询数据库时取消设置它,这将使您能够直接打印返回的记录,例如

To unset the _id when querying and then print data in a loop you write something like this

要在查询时取消设置 _id 然后在循环中打印数据,您可以编写如下内容

records = mycollection.find(query, {'_id': 0}) #second argument {'_id':0} unsets the id from the query
for record in records:
    print(record)