প্রকারের ত্রুটি: অবজেক্টআইড ('') জেএসএন সিরিয়ালযোগ্য নয় able


109

পাইথন ব্যবহার করে দস্তাবেজে একটি সামগ্রিক ফাংশন অনুসন্ধান করার পরে মঙ্গোডিবি থেকে আমার প্রতিক্রিয়া ফিরে আসে, এটি বৈধ প্রতিক্রিয়া দেয় এবং আমি এটি মুদ্রণ করতে পারি তবে এটি ফেরত দিতে পারি না।

ত্রুটি:

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

এটি বিশ্রামের কল:

@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

ডিবি ভালভাবে সংযুক্ত আছে এবং সংগ্রহও রয়েছে এবং আমি বৈধ প্রত্যাশিত ফলাফল ফিরে পেয়েছি তবে যখন আমি এটি ফেরত দেওয়ার চেষ্টা করি তখন আমাকে জসন ত্রুটি দেয়। কীভাবে প্রতিক্রিয়াটিকে আবার JSON এ রূপান্তর করতে হয়। ধন্যবাদ

উত্তর:


118

আপনার নিজের 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)

পারফেক্ট! এটা আমার জন্য কাজ করে। আমার ইতিমধ্যে একটি জসন এনকোডার শ্রেণি রয়েছে, আমি কীভাবে তা আপনার ক্লাসের সাথে একীভূত করতে পারি? আমার ইতিমধ্যে জসন এনকোড শ্রেণি: 'ক্লাস মাইজসন এনকোডার (json.JSONEncoder): ডিফল্ট ডিফল্ট (স্ব, আপত্তি): যদি আইসনস্ট্যান্স (আপত্তি, তারিখের সময়): প্রত্যাবর্তন str (obj.strftime ("% Y-% m-% d% H:% M:% S")) json.JSONEncoder.default (স্ব, আপত্তি) '
ইরফান

1
@IrfanDayan, শুধু যোগ if isinstance(o, ObjectId): return str(o)সামনে returnপদ্ধতিতে default
Defuz

2
আপনি কি যুক্ত করতে পারবেন from bson import ObjectId, যাতে প্রত্যেকে আরও দ্রুত কপি-পেস্ট করতে পারে? ধন্যবাদ!
Liviu Chircu

@ ডিফুজ কেন শুধু ব্যবহার strকরবেন না ? এই পদ্ধতির সাথে কী ভুল?
কেভিন

@ ডেফুজ: আমি যখন এটি ব্যবহার করার চেষ্টা করি তখন অবজেক্টআইডি সরিয়ে ফেলা হয় তবে আমার জসন প্রতিক্রিয়াটি একক অক্ষরে বিভক্ত হয়ে যায়। আমি বোঝাতে চাইছি যখন আমি ফলাফলের json থেকে লুপের জন্য প্রতিটি উপাদান মুদ্রণ করি তখন আমি প্রতিটি চরিত্রকে উপাদান হিসাবে পাই। এই সমাধান করার কোন ধারণা?
ভারিজ কপিল

119

পিমোঙ্গো json_util সরবরাহ করে - আপনি BSON প্রকারগুলি পরিচালনা করতে পরিবর্তে একটিটি ব্যবহার করতে পারেন


আমি @ টিমের সাথে একমত, মঙ্গো থেকে আসা বিএসওএন ডেটার সাথে ডিল করার এটি সঠিক উপায়। api.mongodb.org/python/current/api/bson/json_util.html
জোশুয়া পাওয়েল

হ্যাঁ, আমরা যদি এইভাবে ব্যবহার করি তবে ঝামেলা মুক্ত বলে মনে হচ্ছে
jonprasetyo

এটি আসলে সেরা উপায়।
রাহুল

14
এখানে একটি উদাহরণটি আরও কিছুটা সহায়ক হবে, কারণ এটি সর্বোত্তম উপায় তবে লিঙ্কযুক্ত ডকুমেন্টেশন নুবসের পক্ষে সবচেয়ে বেশি বন্ধুত্বপূর্ণ নয়
জ্যাক

2
from bson import json_util json.loads(json_util.dumps(user_collection)) ^ এটি পাইথন-বিসোনজ ইনস্টল করার পরে কাজ করেছেpipenv install python-bsonjs
এনভ্যাট

38
>>> 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 থেকে প্রকৃত উদাহরণ ।

ফ্লাস্কের জসনফিফের বিপরীতে, "ডাম্পগুলি" একটি স্ট্রিং ফিরিয়ে দেবে, সুতরাং এটি ফ্লাস্কের জসোনিফাইয়ের 1: 1 প্রতিস্থাপন হিসাবে ব্যবহার করা যাবে না।

তবে এই প্রশ্নটি দেখায় যে আমরা json_util.dumps () ব্যবহার করে সিরিয়ালাইজ করতে পারি, json.loads () ব্যবহার করে ডিকটিতে ফিরে রূপান্তর করতে পারি এবং অবশেষে এটিতে ফ্লাস্কের জসনফি কল করতে পারি।

উদাহরণ (পূর্ববর্তী প্রশ্নের উত্তর থেকে প্রাপ্ত):

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

এই সমাধানটি অবজেক্টআইডি এবং অন্যদের (যেমন বাইনারি, কোড ইত্যাদি) "স্ট্রিং সমতুল্য যেমন" $ oid "তে রূপান্তর করবে।

JSON আউটপুটটি দেখতে এই রকম হবে:

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

কেবল পরিষ্কার করার জন্য, সরাসরি ফ্লাস্কের অনুরোধ হ্যান্ডলারের কাছ থেকে 'জসনিফাই' কল করার দরকার নেই - কেবল স্যানিটাইজড ফলাফলটি ফিরিয়ে দিন।
oferei

তুমি একদমই সঠিক. একটি পাইথন ডিক (যা json.loads ফেরত দেয়) স্বয়ংক্রিয়ভাবে ফ্লাস্ক দ্বারা jsonified করা উচিত।
গ্যারেন এস

একটি ডিক অবজেক্ট কল না কি?
সৌভিকমাজি

@ rick112358 ডিকের কল না হওয়া এই প্রশ্নোত্তরের সাথে কীভাবে সম্পর্কিত?
গ্যারেন এস

ঠিক একই অভিধানটি ফিরে পেতে আপনি 'json_util.loads () ব্যবহার করতে পারেন (' id ওড 'কী এর পরিবর্তে)।
rGun

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

এটি বিএসনকে জেএসএন বস্তুতে রূপান্তর করার নমুনা উদাহরণ। আপনি এটি চেষ্টা করতে পারেন।


21

বেশিরভাগ ব্যবহারকারী যারা "জেএসওএন সিরিয়ালাইজযোগ্য নয়" ত্রুটি default=strপান তাদের ব্যবহার করার সময় কেবল নির্দিষ্ট করা দরকার json.dumps। উদাহরণ স্বরূপ:

json.dumps(my_obj, default=str)

এটি strত্রুটি প্রতিরোধ করে রূপান্তরকে বাধ্য করবে । অবশ্যই তারপরে উত্পন্ন আউটপুটটি দেখুন এটি নিশ্চিত করুন যে এটি আপনার প্রয়োজন।


16

একটি দ্রুত প্রতিস্থাপন হিসাবে, আপনি পরিবর্তন করতে পারেন {'owner': objectid}থেকে{'owner': str(objectid)}

তবে আপনার নিজের সংজ্ঞা দেওয়া JSONEncoderআরও ভাল সমাধান, এটি আপনার প্রয়োজনীয়তার উপর নির্ভর করে।


6

আমার মনে হয় এখানে পোস্ট করা এটির Flaskসাথে ব্যবহার করা লোকের পক্ষে কার্যকর হতে পারেpymongo । এটি ফ্ল্যাশকে মার্শাল পাইমঙ্গো বিসন ডেটা প্রকারের মার্শালগুলিতে অনুমতি দেওয়ার জন্য আমার বর্তমান "সেরা অনুশীলন" সেটআপ up

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 বা মোংড প্রসারিত জেএসএন পরিবর্তে কেন এটি করবেন ?

আমি মনে করি মঙ্গো বিশেষ জেএসওন পরিবেশন ক্লায়েন্ট অ্যাপ্লিকেশনগুলিতে বোঝা চাপিয়ে দেয়। বেশিরভাগ ক্লায়েন্ট অ্যাপ্লিকেশন কোনও জটিল উপায়ে মঙ্গো অবজেক্টগুলি ব্যবহার করে না। যদি আমি প্রসারিত জসন পরিবেশন করি তবে এখন আমাকে এটির সার্ভার সাইড এবং ক্লায়েন্ট সাইডটি ব্যবহার করতে হবে। ObjectIdএবং Timestampস্ট্রিং হিসাবে কাজ করা সহজ এবং এটি এই সমস্ত মঙ্গো মার্শালিং পাগলামিকে সার্ভারের সাথে পৃথক করে রাখে।

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

আমি মনে করি এটি বেশিরভাগ অ্যাপ্লিকেশনগুলির সাথে কাজ করতে কম সাহসী ।

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

4

এইভাবে আমি সম্প্রতি ত্রুটিটি ঠিক করেছি

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

এক্ষেত্রে আপনি '_id' বৈশিষ্ট্যটি পাস করছেন না, পরিবর্তে কেবল '_id' মুছে ফেলেছেন এবং ডকের অন্যান্য বৈশিষ্ট্যগুলি পাস করেছেন
মুহ্রিদ্দিন ইসমাইলভ

3

আমি জানি আমি দেরি করে পোস্ট করছি তবে ভেবেছিলাম এটি কমপক্ষে কয়েকজন লোককে সাহায্য করবে!

টিম এবং ডিফুজ দ্বারা উল্লিখিত দুটি উদাহরণ (যা শীর্ষে ভোট দেওয়া হয়) পুরোপুরি সূক্ষ্মভাবে কাজ করে। তবে, এক মিনিটের পার্থক্য রয়েছে যা সময়ে সময়ে তাৎপর্যপূর্ণ হতে পারে।

  1. নিম্নলিখিত পদ্ধতিতে একটি অতিরিক্ত ক্ষেত্র যুক্ত হয়েছে যা অনর্থক এবং এটি সব ক্ষেত্রেই আদর্শ নাও হতে পারে

পিমোঙ্গো json_util সরবরাহ করে - আপনি BSON প্রকারগুলি পরিচালনা করতে পরিবর্তে একটিটি ব্যবহার করতে পারেন

আউটপুট: {"_আইডি": {"$ ওআইডি": "abc123"}

  1. যেখানে JsonEncoder শ্রেণি আমাদের প্রয়োজন মতো স্ট্রিং ফর্ম্যাটে একই আউটপুট দেয় এবং আমাদের পাশাপাশি json.loads (আউটপুট) ব্যবহার করতে হবে। কিন্তু এটি বাড়ে

আউটপুট: {"_id": "abc123"}

যদিও প্রথম পদ্ধতিটি সহজ দেখাচ্ছে, উভয় পদ্ধতির জন্য খুব কম প্রচেষ্টা দরকার।


pytest-mongodbফিক্সচার তৈরি করার সময় এটি প্লাগইনটির জন্য খুব দরকারী
tsveti_iko

3

আমার ক্ষেত্রে আমার এর মতো কিছু দরকার ছিল:

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

1
+1 হা! এটি আরও সহজ হতে পারে 😍 সাধারণভাবে বলতে গেলে; কাস্টম এনকোডার এবং বিসন আমদানি সহ সমস্ত ধোঁকা এড়ানোর জন্য , অবজেক্ট স্ট্রিংয়ে কাস্ট করুন :object['_id'] = str(object['_id'])
ভ্যাক্সি

2

JSON সুরক্ষা বর্ণিত হিসাবে ফ্লাস্কের জসোনিফাই সুরক্ষা বর্ধন সরবরাহ করে । যদি কাস্টম এনকোডারটি ফ্লাস্কের সাথে ব্যবহার করা হয়, তবে JSON সুরক্ষায় আলোচিত বিষয়গুলি বিবেচনা করা আরও ভাল


2

আমি একটি অতিরিক্ত সমাধান সরবরাহ করতে চাই যা গৃহীত উত্তরের উন্নতি করবে। আমি আগে এখানে অন্য থ্রেডে উত্তরগুলি সরবরাহ করেছি ।

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

1

আপনার যদি রেকর্ডগুলির _id প্রয়োজন না হয় তবে আমি ডিবি কে জিজ্ঞাসাবাদ করার সময় এটি পুনরায় সেট করার প্রস্তাব দিচ্ছি যা আপনাকে ফিরে আসা রেকর্ডগুলি সরাসরি মুদ্রণ করতে সক্ষম করবে উদাহরণস্বরূপ

জিজ্ঞাসা করার সময় _id আনসেট করতে এবং তারপরে একটি লুপে ডেটা মুদ্রণ করতে আপনি এই জাতীয় কিছু লিখেন

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

0

এর জন্য সমাধান: মঙ্গোইজিন + মার্সমেলো

আপনি যদি ব্যবহার করেন 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")

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

0

আপনি যদি _idপ্রতিক্রিয়া না চান , আপনি নিজের কোডটিকে এরকম কিছু করতে পারেন:

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

এটি TypeError: ObjectId('') is not JSON serializableত্রুটিটি সরিয়ে ফেলবে ।

আমাদের সাইট ব্যবহার করে, আপনি স্বীকার করেছেন যে আপনি আমাদের কুকি নীতি এবং গোপনীয়তা নীতিটি পড়েছেন এবং বুঝতে পেরেছেন ।
Licensed under cc by-sa 3.0 with attribution required.