কীভাবে JSON সেটগুলি সিরিয়ালাইজ করবেন?


148

আমার একটি পাইথন setরয়েছে যাতে সংগ্রহের মধ্যে কোনও নকলকে অন্তর্ভুক্ত করা না যায় সে জন্য নির্দিষ্ট পদার্থ __hash__এবং __eq__পদ্ধতি রয়েছে methods

আমি JSON সঙ্কেতাক্ষরে লিখা এই ফলাফলের প্রয়োজন set, কিন্তু একটি খালি এমনকি ক্ষণস্থায়ী setকরার json.dumpsপদ্ধতি উত্থাপন TypeError

  File "/usr/lib/python2.7/json/encoder.py", line 201, in encode
    chunks = self.iterencode(o, _one_shot=True)
  File "/usr/lib/python2.7/json/encoder.py", line 264, in iterencode
    return _iterencode(o, 0)
  File "/usr/lib/python2.7/json/encoder.py", line 178, in default
    raise TypeError(repr(o) + " is not JSON serializable")
TypeError: set([]) is not JSON serializable

আমি জানি যে আমি json.JSONEncoderক্লাসে একটি এক্সটেনশন তৈরি করতে পারি যার একটি কাস্টম defaultপদ্ধতি আছে তবে আমি নিশ্চিত নই যে কোথা থেকে রূপান্তর করতে শুরু করব set। আমি setকি ডিফল্ট পদ্ধতিতে মানগুলির বাইরে একটি অভিধান তৈরি করব এবং তারপরে এনকোডিংটি ফিরিয়ে দেব ? আদর্শভাবে, আমি ডিফল্ট পদ্ধতিটি এমন সমস্ত ডেটাটাইপগুলি পরিচালনা করতে সক্ষম করতে চাই যা মূল এনকোডারটি চিক করে তোলে (আমি মঙ্গোকে ডেটা উত্স হিসাবে ব্যবহার করছি যাতে তারিখগুলিও এই ত্রুটিটি বাড়িয়ে তোলে)

সঠিক দিকের কোনও ইঙ্গিত প্রশংসা করা হবে।

সম্পাদনা করুন:

উত্তর করার জন্য ধন্যবাদ! সম্ভবত আমার আরও সুনির্দিষ্ট হওয়া উচিত ছিল।

আমি setঅনুবাদগুলি অনুবাদ করার সীমাবদ্ধতাটি পেতে এখানে (এবং উন্নত) ব্যবহার করেছি তবে অভ্যন্তরীণ কীগুলিও এটি একটি সমস্যা are

এর মধ্যে থাকা setঅবজেক্টগুলি হ'ল জটিল অবজেক্ট যা অনুবাদ করে __dict__তবে তাদের নিজস্ব বৈশিষ্ট্যগুলির জন্য মানগুলি থাকতে পারে যা জসন এনকোডারটিতে মূল ধরণের জন্য অযোগ্য হতে পারে।

এটির মধ্যে বিভিন্ন ধরণের প্রচুর আগমন ঘটে এবং setহ্যাশটি মূলত সত্তার জন্য একটি অনন্য আইডি গণনা করে তবে নোএসকিউএল-এর সত্য আত্মায় সন্তানের অবজেক্টটি কী রয়েছে তা সঠিকভাবে বলতে পারে না।

একটি বস্তুর জন্য একটি তারিখের মান থাকতে পারে starts, অন্যদিকে কিছু অন্যান্য স্কিমা থাকতে পারে যার মধ্যে "অ-আদিম" অবজেক্টযুক্ত কোনও কী নেই।

সে কারণেই আমি কেবলমাত্র সমাধানটিই ভাবতে পারি যে বিভিন্ন কেস চালু JSONEncoderকরার defaultপদ্ধতিটি প্রতিস্থাপনের প্রসারকে বাড়ানো ছিল - তবে আমি কীভাবে এই বিষয়ে যেতে হবে তা নিশ্চিত নই এবং ডকুমেন্টেশন দ্বিপাক্ষিক is নেস্টেড অবজেক্টগুলিতে, defaultকীটি কী দ্বারা ফিরে আসা থেকে ফিরে আসে , বা এটি কেবল একটি জেনেরিককে অন্তর্ভুক্ত / বাতিল করে দেয় যা পুরো অবজেক্টকে দেখায়? কীভাবে সেই পদ্ধতিতে নেস্টেড মানগুলি সমন্বিত হয়? আমি পূর্ববর্তী প্রশ্নগুলি দেখেছি এবং কেস-নির্দিষ্ট এনকোডিংয়ের জন্য সর্বোত্তম পদ্ধতির সন্ধান করতে পারে না (যা দুর্ভাগ্যবশত মনে হয় যে এখানে আমার কী করা দরকার)।


3
কেন dict? আমি মনে করি আপনি listসেট থেকে কিছুটা দূরে তৈরি করতে চান এবং তারপরে এটি এনকোডারকে দিয়ে দিতে চান ... যেমন:encode(list(myset))
কনস্টান্টিনিয়াস

2
JSON ব্যবহার করার পরিবর্তে, আপনি YAML ব্যবহার করতে পারেন (JSON মূলত YAML এর একটি উপসেট)।
পাওলো মোরেটি

@ পাওলোমোর্ত্তি: যদিও এতে কোনও লাভ হয়? আমি মনে করি না যে ওয়াইএএমএল সর্বজনীন-সমর্থিত ডেটা ধরণের মধ্যে সেটগুলি হ'ল, এবং এটি কম ব্যাপকভাবে সমর্থিত, বিশেষত এপিআই সম্পর্কিত।

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

2
@ ডেলানান আমি ওয়াইএমএল-এর পরামর্শ দিচ্ছিলাম কারণ এতে সেট এবং তারিখ উভয়ের জন্য স্থানীয় সমর্থন রয়েছে ।
পাওলো মোরেটি

উত্তর:


116

জেএসএন স্বরলিপিটিতে কেবল হাতে গোনা কয়েকটি দেশীয় ডেটাটাইপস (অবজেক্টস, অ্যারে, স্ট্রিংস, সংখ্যা, বুলিয়ান এবং নাল) রয়েছে, তাই জেএসএনে সিরিয়ালযুক্ত যে কোনও কিছুই এই ধরণের একটি হিসাবে প্রকাশ করা দরকার।

জসন মডিউল ডক্স হিসাবে দেখানো হয়েছে , এই রূপান্তরটি একটি JSONEncoder এবং JSONDecoder দ্বারা স্বয়ংক্রিয়ভাবে করা যেতে পারে , তবে তারপরে আপনি আপনার প্রয়োজন হতে পারে এমন আরও কিছু কাঠামো ছেড়ে দিবেন (যদি আপনি একটি তালিকায় সেটগুলি রূপান্তর করেন তবে আপনি নিয়মিত পুনরুদ্ধার করার ক্ষমতা হারাবেন তালিকাগুলি; যদি আপনি সেট ব্যবহার করে অভিধানে রূপান্তর করেন dict.fromkeys(s)তবে অভিধানগুলি পুনরুদ্ধার করার ক্ষমতা হারাবেন)।

আরও পরিশীলিত সমাধান হ'ল এমন একটি কাস্টম প্রকার তৈরি করা যা অন্যান্য দেশীয় জেএসওএন প্রকারের সাথে সহাবস্থান করতে পারে। এটি আপনাকে নেস্টেড স্ট্রাকচারগুলিকে সঞ্চয় করতে দেয় যা তালিকাগুলি, সেট, ডিক্টস, দশমিক, তারিখের সময় অবজেক্টস ইত্যাদি অন্তর্ভুক্ত করে .:

from json import dumps, loads, JSONEncoder, JSONDecoder
import pickle

class PythonObjectEncoder(JSONEncoder):
    def default(self, obj):
        if isinstance(obj, (list, dict, str, unicode, int, float, bool, type(None))):
            return JSONEncoder.default(self, obj)
        return {'_python_object': pickle.dumps(obj)}

def as_python_object(dct):
    if '_python_object' in dct:
        return pickle.loads(str(dct['_python_object']))
    return dct

এখানে একটি নমুনা অধিবেশন দেখানো হচ্ছে যে এটি তালিকা, ডিক্টস এবং সেটগুলি পরিচালনা করতে পারে:

>>> data = [1,2,3, set(['knights', 'who', 'say', 'ni']), {'key':'value'}, Decimal('3.14')]

>>> j = dumps(data, cls=PythonObjectEncoder)

>>> loads(j, object_hook=as_python_object)
[1, 2, 3, set(['knights', 'say', 'who', 'ni']), {u'key': u'value'}, Decimal('3.14')]

বিকল্পভাবে, আরও সাধারণ উদ্দেশ্যে সিরিয়ালাইজেশন কৌশল যেমন YAML , টুইস্টেড জেলি বা পাইথনের আচার মডিউলটি ব্যবহার করা কার্যকর হতে পারে । এগুলি প্রতিটি ডেটাটাইপগুলির একটি আরও বৃহত্তর পরিসীমা সমর্থন করে।


11
এটিই আমি প্রথম শুনেছি যে জেএমএনের চেয়ে ওয়াইএএমএল আরও সাধারণ উদ্দেশ্য ... o_O
কার্ল ন্যাচেল

13
@ কার্লকনেটস্টেল ওয়াইএএমএল জেএসএনের সুপারস্টার (খুব কাছাকাছি)। এটি বাইনারি ডেটা, সেট, অর্ডার করা মানচিত্র এবং টাইমস্ট্যাম্পগুলির জন্য ট্যাগও যুক্ত করে। "আরও সাধারণ উদ্দেশ্য" বলতে আমি আরও বেশি ডেটাটাইপ সমর্থন করি। আপনি "সাধারণ উদ্দেশ্য" শব্দটিকে অন্য অর্থে ব্যবহার করছেন বলে মনে হচ্ছে।
রায়মন্ড হেটেঙ্গার 21

4
জাসনপিকলকেও ভুলে যাবেন না , যা জাইসন-তে পাইথন অবজেক্টগুলি বাছাইয়ের জন্য একটি সাধারণ লাইব্রেরি হিসাবে লক্ষ্য করা যায়, যতটা এই উত্তরটি দেখায়।
জেসন আর। কম্বস

4
সংস্করণ 1.2 এর অনুসারে, ওয়াইএএমএল জেএসএনের একটি কঠোর সুপারসেট। সমস্ত আইনী জেএসএন এখন বৈধ ওয়াইএএমএল। yaml.org/spec/1.2/spec.html
স্টিভাহ

2
এই কোড উদাহরণটি আমদানি JSONDecoderকরে কিন্তু এটি ব্যবহার করে না
ওয়াটসোনিক

115

আপনি যে একটি ফেরৎ একটি কাস্টম এনকোডার তৈরি করতে পারেন listযখন এটি একটি encounters set। এখানে একটি উদাহরণ:

>>> import json
>>> class SetEncoder(json.JSONEncoder):
...    def default(self, obj):
...       if isinstance(obj, set):
...          return list(obj)
...       return json.JSONEncoder.default(self, obj)
... 
>>> json.dumps(set([1,2,3,4,5]), cls=SetEncoder)
'[1, 2, 3, 4, 5]'

আপনি অন্য প্রকারগুলিও এইভাবে সনাক্ত করতে পারেন। আপনার যদি তালিকাগুলি আসলে একটি সেট হিসাবে ধরে রাখতে হয় তবে আপনি একটি কাস্টম এনকোডিং ব্যবহার করতে পারেন। এর মতো কিছু return {'type':'set', 'list':list(obj)}কাজ করতে পারে।

নেস্টেড নেস্টেড প্রকারের জন্য, এটি ক্রমিকায়ন বিবেচনা করুন:

>>> class Something(object):
...    pass
>>> json.dumps(set([1,2,3,4,5,Something()]), cls=SetEncoder)

এটি নিম্নলিখিত ত্রুটি উত্থাপন করে:

TypeError: <__main__.Something object at 0x1691c50> is not JSON serializable

এটি ইঙ্গিত দেয় যে এনকোডারটি listফলাফলটি ফিরে আসবে এবং পুনরাবৃত্তভাবে তার বাচ্চাদের উপর সিরিয়ালটিকে কল করবে। একাধিক প্রকারের জন্য একটি কাস্টম সিরিয়ালাইজার যুক্ত করতে, আপনি এটি করতে পারেন:

>>> class SetEncoder(json.JSONEncoder):
...    def default(self, obj):
...       if isinstance(obj, set):
...          return list(obj)
...       if isinstance(obj, Something):
...          return 'CustomSomethingRepresentation'
...       return json.JSONEncoder.default(self, obj)
... 
>>> json.dumps(set([1,2,3,4,5,Something()]), cls=SetEncoder)
'[1, 2, 3, 4, 5, "CustomSomethingRepresentation"]'

ধন্যবাদ, আমি প্রশ্নটি সম্পাদন করে আরও ভালভাবে নির্দিষ্ট করেছিলাম যে এটি আমার প্রয়োজন ধরণের জিনিস। আমি যা বুঝতে পারি না তা হ'ল এই পদ্ধতিটি কীভাবে নেস্টেড অবজেক্টগুলি পরিচালনা করবে। আপনার উদাহরণে রিটার্ন মানটি সেটের জন্য তালিকাভুক্ত, তবে যদি বস্তুটি উত্তীর্ণ হয় তবে তার ভিতরে তারিখগুলি (অন্য কোনও খারাপ ডেটাটাইপ) সমেত একটি সেট থাকে? আমি কি ডিফল্ট পদ্ধতিতে কীগুলি দিয়ে ছিটিয়ে থাকা উচিত? অসংখ্য ধন্যবাদ!
ডিকনডেস্পেরাদো

1
আমি মনে করি জেএসএন মডিউল আপনার জন্য নেস্টেড বস্তুগুলি পরিচালনা করে। এটি তালিকাটি ফিরে পাওয়ার পরে, তালিকাভুক্ত আইটেমগুলির মধ্যে প্রতিটি এনকোড করার চেষ্টা করছে ite যদি তাদের মধ্যে একটি তারিখ হয় তবে defaultফাংশনটি আবার ডাকা হবে , এবার objতারিখের অবজেক্ট হিসাবে, সুতরাং আপনাকে কেবল এটির জন্য পরীক্ষা করতে হবে এবং একটি তারিখ-উপস্থাপনা ফিরে করতে হবে।
জেটেরেস

সুতরাং ডিফল্ট পদ্ধতিটি যে কোনও একটি বস্তুর নিকটে যাওয়ার জন্য অনুগ্রহ করে বেশ কয়েকবার চলতে পারে, যেহেতু এটি "স্বীকৃত" হয়ে গেলে এটি স্বতন্ত্র কীগুলিতেও দেখবে?
ডিকনডেস্পেরাদো

বাছাই করুন, এটি একই বস্তুর জন্য একাধিকবার কল করা হবে না , তবে এটি শিশুদের মধ্যে পুনরাবৃত্তি করতে পারে। আপডেট উত্তর দেখুন।
জেটেরেস

আপনি বর্ণিত হিসাবে ঠিক কাজ করে। আমার এখনও কিছু ত্রুটি খুঁজে বের করতে হবে, তবে বেশিরভাগটি সম্ভবত এমন স্টাফ যা রিফ্যাক্টর করা যায়। আপনার গাইডেন্সের জন্য একটি টন ধন্যবাদ!
ডিকনডেস্পেরাদো

7

আমি পাইথন 3 তে রেমন্ড হেটিংজারের দ্রবণটি অভিযোজিত করেছি ।

যা পরিবর্তিত হয়েছে তা এখানে:

  • unicode অদৃশ্য
  • পিতামাতাদের defaultসাথে কল আপডেট করেছেনsuper()
  • ধরণের base64সিরিয়ালাইজ করতে ব্যবহার bytesকরে str(কারণ মনে হচ্ছে bytesঅজগর 3 এ JSON তে রূপান্তর করা যায় না)
from decimal import Decimal
from base64 import b64encode, b64decode
from json import dumps, loads, JSONEncoder
import pickle

class PythonObjectEncoder(JSONEncoder):
    def default(self, obj):
        if isinstance(obj, (list, dict, str, int, float, bool, type(None))):
            return super().default(obj)
        return {'_python_object': b64encode(pickle.dumps(obj)).decode('utf-8')}

def as_python_object(dct):
    if '_python_object' in dct:
        return pickle.loads(b64decode(dct['_python_object'].encode('utf-8')))
    return dct

data = [1,2,3, set(['knights', 'who', 'say', 'ni']), {'key':'value'}, Decimal('3.14')]
j = dumps(data, cls=PythonObjectEncoder)
print(loads(j, object_hook=as_python_object))
# prints: [1, 2, 3, {'knights', 'who', 'say', 'ni'}, {'key': 'value'}, Decimal('3.14')]

4
সম্পর্কিত প্রশ্নের এই উত্তরের শেষে প্রদর্শিত কোডটি [কেবল] ডিকোডিং এবং এনকোড করে বাইটস অবজেক্টটি json.dumps()ফিরে / থেকে এনকোড করে প্রয়োজনীয় জিনিসটি 'latin1'এড়িয়ে যায় base64
মার্টিনিউ

6

জেএসএনে কেবল অভিধান, তালিকা এবং আদিম বস্তুর প্রকার (ইনট, স্ট্রিং, বুল) উপলব্ধ।


5
পাইথনের কথা বলার সময় "আদিম বস্তুর ধরণ" কোনও অর্থ দেয় না। "অন্তর্নির্মিত অবজেক্ট" আরও বোধগম্য করে, তবে এখানে খুব বিস্তৃত (শুরুর জন্য: এতে ডিক্টস, তালিকা এবং সেটগুলিও অন্তর্ভুক্ত রয়েছে)। (জেএসওএন টার্মিনোলজি যদিও আলাদা হতে পারে

স্ট্রিং নম্বর অবজেক্ট অ্যারে সত্য মিথ্যা নাল
জোসেফ লে ব্রিচ

6

defaultপদ্ধতিটি সরবরাহ করতে আপনার কাস্টম এনকোডার শ্রেণি তৈরি করার দরকার নেই - এটি কীওয়ার্ড আর্গুমেন্ট হিসাবে পাস করা যেতে পারে:

import json

def serialize_sets(obj):
    if isinstance(obj, set):
        return list(obj)

    return obj

json_str = json.dumps(set([1,2,3]), default=serialize_sets)
print(json_str)

[1, 2, 3]সমস্ত সমর্থিত পাইথন সংস্করণে ফলাফল ।


4

আপনার যদি কেবল পাইথন অবজেক্ট নয়, কেবল সেটগুলি এনকোড করা দরকার এবং এটিকে সহজেই মানব-পঠনযোগ্য রাখতে চান, তবে রেমন্ড হেটিঙ্গারের উত্তরের একটি সরল সংস্করণ ব্যবহার করা যেতে পারে:

import json
import collections

class JSONSetEncoder(json.JSONEncoder):
    """Use with json.dumps to allow Python sets to be encoded to JSON

    Example
    -------

    import json

    data = dict(aset=set([1,2,3]))

    encoded = json.dumps(data, cls=JSONSetEncoder)
    decoded = json.loads(encoded, object_hook=json_as_python_set)
    assert data == decoded     # Should assert successfully

    Any object that is matched by isinstance(obj, collections.Set) will
    be encoded, but the decoded value will always be a normal Python set.

    """

    def default(self, obj):
        if isinstance(obj, collections.Set):
            return dict(_set_object=list(obj))
        else:
            return json.JSONEncoder.default(self, obj)

def json_as_python_set(dct):
    """Decode json {'_set_object': [1,2,3]} to set([1,2,3])

    Example
    -------
    decoded = json.loads(encoded, object_hook=json_as_python_set)

    Also see :class:`JSONSetEncoder`

    """
    if '_set_object' in dct:
        return set(dct['_set_object'])
    return dct

1

আপনার যদি কেবল দ্রুত ডাম্প প্রয়োজন হয় এবং কাস্টম এনকোডারটি প্রয়োগ করতে চান না। আপনি নিম্নলিখিত ব্যবহার করতে পারেন:

json_string = json.dumps(data, iterable_as_array=True)

এটি সমস্ত সেট (এবং অন্যান্য পুনরাবৃত্ত )গুলিকে অ্যারে রূপান্তর করবে। আপনি জসনকে পিছনে পার্স করার সময় কেবল সাবধান হন যে সেই ক্ষেত্রগুলি অ্যারে থাকবে। আপনি যদি প্রকারগুলি সংরক্ষণ করতে চান তবে আপনাকে কাস্টম এনকোডার লিখতে হবে।


7
আমি যখন এটি চেষ্টা করি তখন পাই: টাইপ এরির: __init __ () একটি অপ্রত্যাশিত মূলশব্দ আর্গুমেন্ট 'পুনরাবৃত্তি_আস_আরে' পেয়েছে
এটিএম

আপনাকে সিম্পজসন ইনস্টল করতে হবে
জেরি ব্রিংগার

সিম্পসসনকে জসন হিসাবে আমদানি করুন এবং তারপরে জসন_স্ট্রিং = জসন.ডাম্পস (ডেটা, পুনরাবৃত্তিযোগ্য_আররে = সত্য) পাইথন ৩.6
তে

1

গৃহীত সমাধানের একটি অভাব হ'ল এর আউটপুটটি খুব অজগর নির্দিষ্ট। অর্থাৎ এর কাঁচা জসন আউটপুট কোনও মানুষ দ্বারা পর্যবেক্ষণ করা যায় না বা অন্য কোনও ভাষা দ্বারা বোঝা যায় (যেমন জাভাস্ক্রিপ্ট)। উদাহরণ:

db = {
        "a": [ 44, set((4,5,6)) ],
        "b": [ 55, set((4,3,2)) ]
        }

j = dumps(db, cls=PythonObjectEncoder)
print(j)

আপনি পাবেন:

{"a": [44, {"_python_object": "gANjYnVpbHRpbnMKc2V0CnEAXXEBKEsESwVLBmWFcQJScQMu"}], "b": [55, {"_python_object": "gANjYnVpbHRpbnMKc2V0CnEAXXEBKEsCSwNLBGWFcQJScQMu"}]}

আমি এমন একটি প্রস্তাব প্রস্তাব করতে পারি যা সেটটি বাইরে যাওয়ার পথে একটি তালিকা থাকা ডিকের কাছে সেটটি ডাউনগ্রেড করে এবং একই এনকোডার ব্যবহার করে অজগরে লোড করার পরে সেটটিতে ফিরে যায়, সুতরাং পর্যবেক্ষণযোগ্যতা এবং ভাষা অজ্ঞাতত্ত্ববাদ সংরক্ষণ করে:

from decimal import Decimal
from base64 import b64encode, b64decode
from json import dumps, loads, JSONEncoder
import pickle

class PythonObjectEncoder(JSONEncoder):
    def default(self, obj):
        if isinstance(obj, (list, dict, str, int, float, bool, type(None))):
            return super().default(obj)
        elif isinstance(obj, set):
            return {"__set__": list(obj)}
        return {'_python_object': b64encode(pickle.dumps(obj)).decode('utf-8')}

def as_python_object(dct):
    if '__set__' in dct:
        return set(dct['__set__'])
    elif '_python_object' in dct:
        return pickle.loads(b64decode(dct['_python_object'].encode('utf-8')))
    return dct

db = {
        "a": [ 44, set((4,5,6)) ],
        "b": [ 55, set((4,3,2)) ]
        }

j = dumps(db, cls=PythonObjectEncoder)
print(j)
ob = loads(j)
print(ob["a"])

যা আপনাকে পায়:

{"a": [44, {"__set__": [4, 5, 6]}], "b": [55, {"__set__": [2, 3, 4]}]}
[44, {'__set__': [4, 5, 6]}]

নোট করুন যে কোনও অভিধানে একটি কী এর সাথে একটি উপাদান রয়েছে "__set__"তাকে ক্রিয়াকলাপ করা এই প্রক্রিয়াটিকে ভেঙে দেবে। সুতরাং __set__এখন একটি সংরক্ষিত dictচাবি পরিণত হয়েছে । স্পষ্টতই অন্যটি ব্যবহার করতে নির্দ্বিধায়, আরও গভীরভাবে আবদ্ধ কী key

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