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
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는 잘 연결되어 있고 컬렉션도 있어 유효한 예상 결과를 얻었지만 반환하려고 하면 Json 오류가 발생합니다.응답을 다시 JSON으로 변환하는 방법을 알고 계십니까?감사해요.
Pymongo는 json_util을 제공합니다.대신 BSON 유형을 처리할 수 있습니다.
def parse_json(data):
return json.loads(json_util.dumps(data))
다음과 같이, 독자적인 것을 정의할 필요가 있습니다.
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)
>>> 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의 실제 예시입니다.
플라스크의 jsonify와 달리 "덤프"는 문자열을 반환하므로 플라스크의 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"
}
}
"not JSON serializable" 오류가 발생한 대부분의 사용자는 사용 시기를 지정하기만 하면 됩니다.예를 들어 다음과 같습니다.
json.dumps(my_obj, default=str)
하면 됩니다.str
에러를 방지합니다.물론 생성된 출력을 보고 필요한 출력인지 확인합니다.
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
고객의 요건에 따라 다릅니다.
, 이 을 쓰는 될 것 같습니다.Flask
pymongo
이것은 플라스크가 pymongo bson 데이터 타입을 마샬링할 수 있도록 하기 위한 현재의 "베스트 프랙티스" 설정입니다.
몽고플라스크화이
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
BSON이나 mongod 확장 JSON을 사용하지 않고 이렇게 하는 이유는 무엇입니까?
을 서빙하는 것은 mongo special JSON에 이 된다고 합니다.대부분의 클라이언트 앱은 mongo 오브젝트를 복잡한 방법으로 사용하는 것을 신경쓰지 않습니다.extended json 。 ObjectId
★★★★★★★★★★★★★★★★★」Timestamp
몽고 마샬링
{
"_id": "5b6b6959828619572d48a9da",
"created_at": "2018-08-08T22:06:17Z"
}
대부분의 어플리케이션에서 작업하는 것보다 부담이 적다고 생각합니다.
{
"_id": {"$oid": "5b6b6959828619572d48a9da"},
"created_at": {"$date": 1533837843000}
}
플라스크를 사용하여 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)
다음을 시도해 보십시오.
objectid = str("51948e86c25f4b1d1c0d303c")
내 경우엔 이런 게 필요했어
class JsonEncoder():
def encode(self, o):
if '_id' in o:
o['_id'] = str(o['_id'])
return o
이것이 내가 최근에 오류를 수정한 방법이다.
@app.route('/')
def home():
docs = []
for doc in db.person.find():
doc.pop('_id')
docs.append(doc)
return jsonify(docs)
늦은 건 알지만 적어도 몇몇 사람들에게 도움이 될 거라고 생각했어요!
tim과 defuz(최상위 투표)가 언급한 예시는 모두 완벽하게 잘 작동합니다.그러나 때로는 유의한 미세한 차이가 있을 수 있습니다.
- 다음 방법에서는 중복된 필드를 하나 더 추가하며 모든 경우에 이상적이지 않을 수 있습니다.
Pymongo는 json_util을 제공합니다.대신 BSON 유형을 처리할 수 있습니다.
출력: { "_id": { "$oid": "abc123" } }
- 여기서 Json Encoder 클래스는 필요한 것과 동일한 출력을 문자열 형식으로 제공하며, 추가로 json.loads(output)를 사용해야 합니다.하지만 그것은
출력: {"_id": "abc123"}
첫 번째 방법은 간단해 보이지만 두 방법 모두 최소한의 노력이 필요합니다.
수용된 답변을 개선할 수 있는 추가 솔루션을 제공하고 싶습니다.나는 이미 여기에 다른 스레드로 답을 제공했습니다.
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()
레코드의 _id가 필요하지 않은 경우 DB를 조회할 때 설정을 해제하여 반환된 레코드를 직접 인쇄할 수 있도록 합니다.
조회할 때 _id를 설정 해제하고 루프에서 데이터를 인쇄하려면 다음과 같이 씁니다.
records = mycollection.find(query, {'_id': 0}) #second argument {'_id':0} unsets the id from the query
for record in records:
print(record)
JSON 응답으로 전송하려면 두 단계로 포맷해야 합니다.
- 「」를 사용합니다.
json_util.dumps()
에서 은밀히ObjectId
BONE 호환성 형식으로 JSON에 대응하여 JON 맷 한 inson다니 to대입 j format bible에 i포 j즉답응son, beson response환호 compatson inson"_id": {"$oid": "123456789"}
The above JSON Response obtained from 위의 JSON 응답은 에서 취득한 것입니다.json_util.dumps()
will have backslashes and quotes에 백슬래시와 따옴표가 붙습니다.
- To remove backslashes and quotes use 백슬래시 및 따옴표를 제거하려면
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"}]
Flask의 jsonify는 JSON Security에서 설명한 바와 같이 보안 강화를 제공합니다.커스텀 인코더를 Flask와 함께 사용하는 경우 JSON Security에서 설명하는 사항을 고려하는 것이 좋습니다.
If you don't want 당신이 원하지 않는다면_id
응답에서 이 같은 코드를 반영할 수 있습니다.응답으로 다음과 같이 코드를 리팩터링할 수 있습니다.
jsonResponse = getResponse(mock_data)
del jsonResponse['_id'] # removes '_id' from the final response
return jsonResponse
이렇게 하면TypeError: ObjectId('') is not JSON serializable
에러입니다.
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))
솔루션: mongoengine + 마시멜로
사용하시는 경우mongoengine
그리고.marshamallow
이 솔루션이 고객님께 적용될 수 있습니다.
기본적으로 저는String
마시멜로의 필드, 디폴트 값을 덮어썼습니다.Schema id
되려고String
부호화되었습니다.
from marshmallow import Schema
from marshmallow.fields import String
class FrontendUserSchema(Schema):
id = String()
class Meta:
fields = ("id", "email")
언급URL : https://stackoverflow.com/questions/16586180/typeerror-objectid-is-not-json-serializable
'programing' 카테고리의 다른 글
텍스트 영역 입력에서 json 데이터 예쁘게 만들기 (0) | 2023.03.09 |
---|---|
Angular Clear 보조양식 데이터 및 재설정 유효성 검사 (0) | 2023.03.09 |
javascript 객체 또는 어레이를 ajax 데이터의 json으로 변환 (0) | 2023.03.09 |
각도에서 jquery 대화상자를 여는 가장 좋은 방법은 무엇입니까? (0) | 2023.03.09 |
창 innerWidth의 AngularJS 이벤트 크기 변경 (0) | 2023.03.09 |