[python] 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으로 다시 변환하는 방법에 대한 아이디어. 감사



답변

자신이 소유 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)


답변

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로 대체 할 수 없습니다.

그러나이 질문 은 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 및 기타 (예 : Binary, Code 등)를 “$ 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 객체로 변환하는 샘플 예제입니다. 이것을 시도 할 수 있습니다.


답변

은 “하지 JSON의 직렬화”오류가 발생 대부분의 사용자는 단순히 지정해야합니다 default=str사용하는 경우 json.dumps. 예를 들면 :

json.dumps(my_obj, default=str)

이렇게하면로 변환 str하여 오류를 방지 할 수 있습니다. 물론 생성 된 출력을보고 그것이 필요한 것인지 확인하십시오.


답변

빠른 교체 {'owner': objectid}{'owner': str(objectid)}.

그러나 자신을 정의 JSONEncoder하는 것이 더 나은 솔루션이며 요구 사항에 따라 다릅니다.


답변

Flask와 함께 사용 하는 사람들에게 유용 할 것이라고 생각하므로 여기에 게시하십시오 pymongo. 이것은 플라스크가 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)

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 특수 JSON을 제공하면 클라이언트 응용 프로그램에 부담이된다고 생각합니다. 대부분의 클라이언트 앱은 복잡한 방식으로 mongo 객체를 사용하는 데 신경 쓰지 않습니다. 확장 json을 제공하면 이제 서버 측과 클라이언트 측을 사용해야합니다. ObjectIdTimestamp문자열로보다 쉽게 작업하고이 서버에 모든 몽고 마샬링 광기 격리를 유지합니다.

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

나는 이것이 대부분의 응용 프로그램 에서 작업하는 것이 덜 부담 스럽다고 생각합니다 .

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