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

2025-03-04 08:25:00
admin
原创
57
摘要:问题描述:我使用 Python 查询文档上的聚合函数后从 MongoDB 返回了响应,它返回了有效的响应,我可以打印它但无法返回它。错误:TypeError: ObjectId('51948e86c25f4b1d1c0d303c') is not JSON serializable 打印:{'result':...

问题描述:

我使用 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 吗?谢谢


解决方案 1:

PyMongo 发行版中的Bson提供了json_util - 你可以使用它来处理 BSON 类型

from bson import json_util

def parse_json(data):
    return json.loads(json_util.dumps(data))

解决方案 2:

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

解决方案 3:

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

json.dumps(my_obj, default=str)

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

解决方案 4:

>>> 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替代品。

但是这个问题表明我们可以使用 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"
  }
}

解决方案 5:

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 对象的示例。你可以尝试一下。

解决方案 6:

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

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

解决方案 7:

我觉得它可能对使用 的人有用,所以在这里发布Flaskpymongo这是我当前的“最佳实践”设置,允许 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)

应用程序

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 扩展的 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:

对于那些需要使用 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)

解决方案 9:

您可以尝试:

对象 ID = str(ObjectId("51948e86c25f4b1d1c0d303c"))

解决方案 10:

就我而言,我需要这样的东西:

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

解决方案 11:

如果要将其作为 JSON 响应发送,则需要分两步进行格式化

  1. 使用json_util.dumps()bson 将ObjectIdBSON 响应转换为 JSON 兼容格式"_id": {"$oid": "123456789"}

上述 JSON 响应将json_util.dumps() 包含反斜杠和引号

  1. 要删除反斜杠和引号,请json.loads()使用json

from bson import json_util
import json

bson_data = [{'_id': ObjectId('123456789'), 'field': 'somedata'},{'_id': ObjectId('123456781'), 'field': 'someMoredata'}]

json_data_with_backslashes = json_util.dumps(bson_data)

# output will look like this
# "[{\"_id\": {\"$oid\": \"123456789\"}, \"field\": \"somedata\"},{\"_id\": {\"$oid\": \"123456781\"}, \"field\": \"someMoredata\"}]"

json_data = json.loads(json_data_with_backslashes)

# output will look like this
# [{"_id": {"$oid": "123456789"},"field": "somedata"},{"_id": {"$oid": "123456781"},"field": "someMoredata"}]

解决方案 12:

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

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

解决方案 13:

我知道我发帖晚了但我认为它至少会帮助一些人!

tim 和 defuz 提到的示例(得票最多)都运行良好。但是,它们之间存在细微的差异,有时这种差异可能非常显著。

  1. 以下方法添加了一个额外的字段,该字段是多余的,并且可能并不适用于所有情况

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

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

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

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

尽管第一种方法看起来很简单,但这两种方法都不需要付出太多努力。

解决方案 14:

我想提供一个额外的解决方案来改进已接受的答案。我之前已在另一个帖子中提供了答案。

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:

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

要在查询时取消设置 _id,然后循环打印数据,你可以这样写

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

解决方案 16:

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

解决方案 17:

如果您不想_id回应,您可以重构您的代码,如下所示:

jsonResponse = getResponse(mock_data)
del jsonResponse['_id'] # removes '_id' from the final response
return jsonResponse

这将消除TypeError: ObjectId('') is not JSON serializable错误。

解决方案 18:

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))

解决方案 19:

    @app.route('/users', methods = ['GET'])
       def get_users():
       online_users = mongo.db.users.find({})
       list_users = list(online_users)
       json_users = json.dumps(list_users, default=json_util.default)
       return Response(json_users, mimetype='application/json')

这就是我让它工作的方式

解决方案 20:

解决方案:mongoengine + marshmallow

如果您使用mongoengine,那么marshamallow该解决方案可能适合您。

基本上,我String从 marshmallow 导入了字段,并覆盖了默认Schema idString编码。

from marshmallow import Schema
from marshmallow.fields import String

class FrontendUserSchema(Schema):

    id = String()

    class Meta:
        fields = ("id", "email")
相关推荐
  政府信创国产化的10大政策解读一、信创国产化的背景与意义信创国产化,即信息技术应用创新国产化,是当前中国信息技术领域的一个重要发展方向。其核心在于通过自主研发和创新,实现信息技术应用的自主可控,减少对外部技术的依赖,并规避潜在的技术制裁和风险。随着全球信息技术竞争的加剧,以及某些国家对中国在科技领域的打压,信创国产化显...
工程项目管理   2787  
  为什么项目管理通常仍然耗时且低效?您是否还在反复更新电子表格、淹没在便利贴中并参加每周更新会议?这确实是耗费时间和精力。借助软件工具的帮助,您可以一目了然地全面了解您的项目。如今,国内外有足够多优秀的项目管理软件可以帮助您掌控每个项目。什么是项目管理软件?项目管理软件是广泛行业用于项目规划、资源分配和调度的软件。它使项...
项目管理软件   1710  
  产品配置管理在企业产品研发与生产过程中扮演着至关重要的角色,它能够确保产品在不同阶段的一致性、可追溯性以及高效的变更管理。PLM(产品生命周期管理)系统作为整合产品全生命周期信息的平台,为产品配置管理提供了强大的支持。随着技术的不断发展,到2025年,PLM系统在支持产品配置管理方面将有一系列值得关注的技术实践。基于人...
plm系统主要干什么的   11  
  敏捷迭代周期与 PLM 流程的适配是现代企业在产品开发过程中面临的重要课题。随着市场竞争的加剧和技术的快速发展,企业需要更加高效、灵活的产品开发模式,以满足客户不断变化的需求。敏捷迭代周期强调快速响应变化、持续交付价值,而 PLM 流程则侧重于产品全生命周期的管理和控制。如何将两者有机结合,优化交付节奏,成为提升企业竞...
plm是什么意思   10  
  在企业的数字化转型进程中,PLM(产品生命周期管理)与ERP(企业资源计划)作为两款重要的企业级系统,发挥着关键作用。然而,很多企业人员对它们之间的区别以及协同逻辑并不十分清晰。深入了解这两者的差异与协同方式,有助于企业更好地规划信息化建设,提升整体运营效率。PLM系统概述PLM系统聚焦于产品从概念设计到退役的全生命周...
国产plm软件   12  
热门文章
项目管理软件有哪些?
曾咪二维码

扫码咨询,免费领取项目管理大礼包!

云禅道AD
禅道项目管理软件

云端的项目管理软件

尊享禅道项目软件收费版功能

无需维护,随时随地协同办公

内置subversion和git源码管理

每天备份,随时转为私有部署

免费试用