TypeError:ObjectId('')不是JSON可序列化的

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了TypeError:ObjectId('')不是JSON可序列化的相关的知识,希望对你有一定的参考价值。

我在使用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

它是RESTful调用:

@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。谢谢

答案

您应该定义自己的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)
另一答案

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

要在查询时取消设置_id,然后在循环中打印数据,请编写类似这样的内容

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

大多数收到“非JSON可序列化”错误的用户只需在使用default=str时指定json.dumps。例如:

json.dumps(my_obj, default=str)

这将强制转换为str,以防止错误。当然,然后查看生成的输出以确认它是您需要的。

另一答案

解决方案:其他+棉花糖

如果您使用mongoenginemarshamallow,那么此解决方案可能适用于您。

基本上,我从棉花糖进口String字段,我覆盖默认的Schema idString编码。

from marshmallow import Schema
from marshmallow.fields import String

class FrontendUserSchema(Schema):

    id = String()

    class Meta:
        fields = ("id", "email")
另一答案

在我的情况下,我需要这样的事情:

class JsonEncoder():
    def encode(self, o):
        if '_id' in o:
            o['_id'] = str(o['_id'])
        return o
另一答案

我想提供一个额外的解决方案,以改善已接受的答案。我以前在另一个线程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()
另一答案

Pymongo提供json_util - 您可以使用它来代替处理BSON类型

另一答案
>>> 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和其他(即二进制,代码等)转换为等效字符串,如“$ oid”。

JSON输出如下所示:


  "_id": 
    "$oid": "abc123"
  

另一答案
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对象的示例示例。你可以试试这个。

另一答案

作为快速替代,您可以将'owner': objectid更改为'owner': str(objectid)

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

另一答案

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

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

我知道我发帖很晚,但认为这对至少有几个人有帮助!

tim和defuz提到的两个例子(最高投票)都很好。然而,有一点微小的差异,有时可能很重要。

  1. 以下方法添加了一个额外的字段,该字段是冗余的,并且在所有情况下可能都不理想

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

输出:“_ id”:“$ oid”:“abc123”

  1. 其中JsonEncoder类以我们需要的字符串格式提供相同的输出,我们还需要使用json.loads(输出)。但它导致了

输出:“_ id”:“abc123”

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

另一答案

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

mongo flask.朋友

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.朋友

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,现在我必须使用它服务器端和客户端。 ObjectIdTimestamp更容易使用字符串,这使得所有这些mongo编组疯狂隔离到服务器。


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

我认为对于大多数应用程序而言,这不太繁琐。


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

另一答案

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

以上是关于TypeError:ObjectId('')不是JSON可序列化的的主要内容,如果未能解决你的问题,请参考以下文章

Node.js TypeError无法读取未定义的属性objectId

Mongo ObjectID:即使使用 pytz,“也无法比较原始偏移量和可感知偏移量的日期时间”

TypeError: 'str' object is not callable

TypeError: 'QueryDict' object is not callable

Python: TypeError: 'dict' object is not callable

Python: TypeError: 'dict' object is not callable