TypeError: ObjectId () ’) JSON क्रमिक नहीं है


109

पाइथन का उपयोग करते हुए दस्तावेज़ पर एक एकत्रित फ़ंक्शन को क्वेरी करने के बाद 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 में कैसे बदला जाए। धन्यवाद

जवाबों:


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 एनकोडर वर्ग है, मैं उसे आपकी कक्षा के साथ कैसे मिला सकता हूं? मेरा पहले से ही Json एनकोड क्लास है: 'वर्ग MyJsonEncoder (json.JSONEncoder): डिफ़ॉल्ट डिफ़ॉल्ट (आत्म, obj): यदि आइंस्टीन (obj, डेटाटाइम): वापसी str (obj.strftime ("% Y-% m-% d% H:% M:% S")) वापसी json.JSONEncoder.default (self, obj)
इरफान

1
@ इरफानदयन, if isinstance(o, ObjectId): return str(o)पहले returnविधि में जोड़ें default
डिफ्यूज

2
क्या आप जोड़ सकते हैं from bson import ObjectId, ताकि हर कोई तेजी से कॉपी-पेस्ट कर सके? धन्यवाद!
लिवियु चिरकु

@defuz सिर्फ उपयोग क्यों नहीं str? उस दृष्टिकोण के साथ क्या गलत है?
केविन

@defuz: जब मैं इसका उपयोग करने की कोशिश करता हूं, तो ऑब्जेक्ट को हटा दिया जाता है, लेकिन मेरे जोंस की प्रतिक्रिया एकल पात्रों में टूट जाती है। मेरा मतलब है कि जब मैं लूप के लिए प्रत्येक तत्व को लूप के लिए प्रिंट करता हूं तो मुझे प्रत्येक चरित्र एक तत्व के रूप में मिलता है। कुछ पता है इसे कैसे हल करना है?
वारिज कपिल

119

Pymongo प्रदान करता है json_util - आप संभाल BSON प्रकार के बजाय उस का उपयोग कर सकते


मैं @tim से सहमत हूं, यह mongo से आने वाले BSON डेटा से निपटने का सही तरीका है। api.mongodb.org/python/current/api/bson/json_util.html
जोशुआ पॉवेल

हां, अगर हम इस तरह का उपयोग करते हैं तो यह परेशानी से मुक्त लगता है
जॉन्सपर्सिलो

यह वास्तव में सबसे अच्छा तरीका है।
राहुल

14
यहाँ एक उदाहरण थोड़ा और अधिक उपयोगी होगा, क्योंकि यह सबसे अच्छा तरीका है लेकिन लिंक किया हुआ दस्तावेज़ noobs के लिए सबसे अधिक उपयोगकर्ता के अनुकूल नहीं है
Jake

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 से वास्तविक उदाहरण ।

फ्लास्क के jsonify के विपरीत, "डंप्स" एक स्ट्रिंग लौटाएगा, इसलिए इसे फ्लास्क के jsonify के 1: 1 प्रतिस्थापन के रूप में उपयोग नहीं किया जा सकता है।

लेकिन यह सवाल दिखाता है कि हम json_util.dumps () का उपयोग करके अनुक्रमित कर सकते हैं, वापस json.loads () का उपयोग करके डिक्टेट में कनवर्ट कर सकते हैं और अंत में उस पर फ्लास्क के 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

यह समाधान ऑब्जेक्टआईड और अन्य (यानी बाइनरी, कोड आदि) को "$ oid" जैसे स्ट्रिंग समकक्ष में बदल देगा।

JSON आउटपुट इस तरह दिखेगा:

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

बस स्पष्ट करने के लिए, फ्लास्क अनुरोध हैंडलर से सीधे 'jsonify' कॉल करने की आवश्यकता नहीं है - बस सैनिटाइज्ड परिणाम लौटाएं।
सेरी

आप बिल्कुल सही कह रहे है। एक पायथन तानाशाह (जो json.loads रिटर्न) को फ्लास्क द्वारा स्वचालित रूप से jsonified किया जाना चाहिए।
गैरेन एस

क्या एक प्रमुख वस्तु कॉल करने योग्य नहीं है?
सौविकमाजी

@ rick112358 एक तानाशाह इस Q & A से संबंधित कैसे नहीं हो सकता है?
गेरेन एस

आप ठीक उसी शब्दकोश को वापस लाने के लिए json_util.loads () का भी उपयोग कर सकते हैं ('oid' कुंजी के साथ एक के बजाय)।
rGun

22

अधिकांश उपयोगकर्ताओं को जो "नहीं JSON serializable" त्रुटि प्राप्त करते हैं, बस default=strउपयोग करते समय निर्दिष्ट करने की आवश्यकता होती है json.dumps। उदाहरण के लिए:

json.dumps(my_obj, default=str)

यह strत्रुटि को रोकने के लिए रूपांतरण को बाध्य करेगा । बेशक तब उत्पन्न आउटपुट को देखें कि यह पुष्टि करने के लिए कि आपको क्या चाहिए।


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)

BSSON को JSON ऑब्जेक्ट में परिवर्तित करने के लिए यह नमूना उदाहरण है। आप यह कोशिश कर सकते हैं।


16

एक त्वरित प्रतिस्थापन के रूप में, आप बदल सकते हैं {'owner': objectid}करने के लिए {'owner': str(objectid)}

लेकिन अपने खुद JSONEncoderको परिभाषित करना एक बेहतर समाधान है, यह आपकी आवश्यकताओं पर निर्भर करता है।


6

यहाँ पोस्टिंग जैसा कि मुझे लगता है कि इसका उपयोग Flaskकरने वाले लोगों के लिए उपयोगी हो सकता है pymongo। यह मेरा वर्तमान "सर्वोत्तम अभ्यास" सेटअप है जो फ्लास्क को मार्शेलो पाइमोंगो बॉन डेटा प्रकारों के लिए अनुमति देता है।

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

बीएसएनएल या मोंगोड विस्तारित जेसन की सेवा के बजाय ऐसा क्यों करते हैं ?

मुझे लगता है कि मूंगो स्पेशल जोंस परोसना क्लाइंट एप्लिकेशन पर बोझ डालता है। अधिकांश क्लाइंट ऐप्स किसी भी जटिल तरीके से मैंगो ऑब्जेक्ट्स का उपयोग करने की परवाह नहीं करेंगे। यदि मैं विस्तारित जसन की सेवा करता हूं, तो अब मुझे इसे सर्वर साइड, और क्लाइंट साइड का उपयोग करना होगा। 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' को हटा दिया गया है और डॉक्टर की अन्य विशेषताओं को पारित कर दिया है
Muhriddin Ismoilov

3

मुझे पता है कि मैं देर से पोस्ट कर रहा हूं, लेकिन लगा कि इससे कम से कम कुछ लोगों को मदद मिलेगी!

टिम और डिफुज़ (जो शीर्ष मतदान हैं) द्वारा उल्लिखित दोनों उदाहरण पूरी तरह से ठीक हैं। हालांकि, एक मिनट का अंतर है जो कई बार महत्वपूर्ण हो सकता है।

  1. निम्न विधि एक अतिरिक्त फ़ील्ड जोड़ती है जो निरर्थक है और सभी मामलों में आदर्श नहीं हो सकती है

Pymongo json_util प्रदान करता है - आप BSON प्रकारों को संभालने के बजाय उस एक का उपयोग कर सकते हैं

आउटपुट: {"_id": {"$ oid": "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 हा! क्या यह अधिक सरल हो सकता था simpl आम तौर पर बोलना; कस्टम एन्कोडर और आयात करने वाले बज़ के साथ सभी फ़ज़ से बचने के लिए, स्ट्रिंग को ऑब्जेक्ट करें :object['_id'] = str(object['_id'])
Vexy

2

फ्लास्क का jsonify JSON Security में वर्णित सुरक्षा वृद्धि प्रदान करता है । यदि कस्टम एनकोडर का उपयोग फ्लास्क के साथ किया जाता है, तो 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 की आवश्यकता नहीं होगी, तो मैं DB को क्वेरी करते समय इसे परेशान करने की सिफारिश करूंगा जो आपको सीधे दिए गए रिकॉर्ड को प्रिंट करने में सक्षम करेगा जैसे

जब क्वेरी करते समय _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 + marshmallow

यदि आप उपयोग करते हैं 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त्रुटि को दूर करेगा ।

हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.