JSON क्रमांकन सेट कैसे करें?


149

मेरे पास एक पायथन setहै जिसमें कुछ डुप्लिकेट्स को संग्रह में शामिल करने के लिए ऑब्जेक्ट्स __hash__और __eq__तरीके शामिल हैं।

मैं इस परिणाम को सांकेतिक शब्दों में बदलना करने की जरूरत है 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अनुवादित होने की सीमाओं के आसपास पाने के लिए यहां (और उत्कीर्ण) उत्तरों का उपयोग किया , लेकिन आंतरिक कुंजी भी हैं जो एक मुद्दा भी हैं।

जिन वस्तुओं में setजटिल वस्तुएं हैं __dict__, वे अनुवाद करते हैं , लेकिन वे स्वयं भी अपने गुणों के लिए मान शामिल कर सकते हैं जो कि json एनकोडर में मूल प्रकारों के लिए अयोग्य हो सकते हैं।

इसमें बहुत सारे अलग-अलग प्रकार आ रहे हैं set, और हैश मूल रूप से इकाई के लिए एक अद्वितीय आईडी की गणना करता है, लेकिन NoSQL की सच्ची भावना में यह नहीं बता रहा है कि वास्तव में बच्चे की वस्तु क्या है।

एक ऑब्जेक्ट में एक दिनांक मान startsहो सकता है, जबकि दूसरे में कुछ अन्य स्कीमा हो सकती हैं जिसमें "गैर-आदिम" ऑब्जेक्ट वाली कोई कुंजी शामिल नहीं है।

यही कारण है कि एकमात्र उपाय जिसके बारे में मैं सोच सकता था, वह यह था कि अलग-अलग मामलों को चालू करने के JSONEncoderलिए defaultविधि को बदलने के लिए - लेकिन मुझे यकीन नहीं है कि इस बारे में कैसे जाना जाए और दस्तावेज अस्पष्ट है। नेस्टेड ऑब्जेक्ट्स में, मान defaultकुंजी से जाने से वापस आता है , या क्या यह केवल एक जेनेरिक शामिल है / पूरी वस्तु को देखता है? यह विधि नेस्टेड मूल्यों को कैसे समायोजित करती है? मैंने पिछले प्रश्नों के माध्यम से देखा है और केस-विशिष्ट एन्कोडिंग के लिए सबसे अच्छा तरीका खोजने के लिए प्रतीत नहीं हो सकता है (जो दुर्भाग्य से ऐसा लगता है कि मैं यहां क्या करने जा रहा हूं)।


3
क्यों dict? मुझे लगता है कि आप listसेट से सिर्फ एक आउट करना चाहते हैं और फिर इसे एनकोडर में पास करना चाहते हैं ... उदाहरण के लिए:encode(list(myset))
कॉन्स्टेंटिनियस

2
JSON का उपयोग करने के बजाय, आप YAML का उपयोग कर सकते हैं (JSON अनिवार्य रूप से YAML का सबसेट है)।
पाओलो मोरेटी

@PaoloMoretti: क्या इससे कोई फायदा होता है? मुझे नहीं लगता कि सेट्स यमल के सार्वभौमिक रूप से समर्थित डेटा प्रकारों में से हैं, और यह विशेष रूप से एपीआई के बारे में कम व्यापक रूप से समर्थित है।

@PaoloMoretti आपके इनपुट के लिए धन्यवाद, लेकिन एप्लिकेशन फ्रंट में JSON को रिटर्न प्रकार की आवश्यकता होती है और यह आवश्यकता सभी उद्देश्यों के लिए निर्धारित है।
डीकॉनडेपरडो 16

2
@delnan मैं YAML का सुझाव दे रहा था क्योंकि इसमें सेट और तारीख दोनों के लिए एक देशी समर्थन है
पाओलो मोरेटी

जवाबों:


116

JSON संकेतन में केवल एक मुट्ठी भर देशी डेटाटिप्स (ऑब्जेक्ट्स, एरेज़, स्ट्रिंग्स, नंबर, बुलियन और नल) होते हैं, इसलिए JSON में धारावाहिक कुछ भी इन प्रकारों में से एक के रूप में व्यक्त किए जाने की आवश्यकता है।

जैसा कि json मॉड्यूल डॉक्स में दिखाया गया है , यह रूपांतरण एक JSONEncoder और JSONDecoder द्वारा स्वचालित रूप से किया जा सकता है , लेकिन तब आपको कुछ अन्य संरचना देनी होगी, जिसकी आपको आवश्यकता हो सकती है (यदि आप सूची में सेट करते हैं, तो आप नियमित रूप से पुनर्प्राप्त करने की क्षमता खो देते हैं। सूचियाँ, (यदि आप सेट का उपयोग कर शब्दकोश में परिवर्तित करते हैं dict.fromkeys(s)तो आप शब्दकोशों को पुनर्प्राप्त करने की क्षमता खो देते हैं)।

एक अधिक परिष्कृत समाधान एक कस्टम प्रकार का निर्माण करना है जो अन्य देशी JSON प्रकारों के साथ सह-अस्तित्व में हो सकता है। यह आपको नेस्टेड संरचनाओं को संग्रहीत करने देता है जिसमें सूचियां, सेट, डिकेट्स, डेसीमल, डेटाइम ऑब्जेक्ट्स आदि शामिल हैं:

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

यहाँ एक नमूना सत्र दिखाया गया है कि यह सूचियों, dicts और सेट को संभाल सकता है:

>>> 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
यह पहली बार मैंने सुना है कि YAML JSON की तुलना में अधिक सामान्य उद्देश्य है ... o_O
कार्ल Knechtel

13
@KarlKnechtel YAML JSON (बहुत लगभग) का सुपरसेट है। यह बाइनरी डेटा, सेट, ऑर्डर किए गए नक्शे और टाइमस्टैम्प के लिए टैग भी जोड़ता है। अधिक डेटाैटिप्स का समर्थन करना, जिसका अर्थ है "अधिक सामान्य उद्देश्य"। आप एक अलग अर्थ में "सामान्य उद्देश्य" वाक्यांश का उपयोग कर रहे हैं।
रेमंड हेटिंगर 21

4
जोसनपिकल को भी मत भूलना , जिसका उद्देश्य पिस्टन की वस्तुओं को JSON में लेने के लिए एक सामान्यीकृत पुस्तकालय होना है, जितना कि यह उत्तर बताता है।
जेसन आर। कोम्ब्स

4
संस्करण 1.2 के रूप में, YAML JSON का एक सख्त सुपरसेट है। सभी कानूनी JSON अब कानूनी YAML है। yaml.org/spec/1.2/spec.html
steveha

2
यह कोड उदाहरण आयात JSONDecoderकरता है लेकिन इसका उपयोग नहीं करता है
watsonic

115

आप एक कस्टम एनकोडर बना सकते हैं जो रिटर्न करता है listजब उसका सामना होता है ए 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"]'

धन्यवाद, मैंने प्रश्न को बेहतर ढंग से संपादित करने के लिए कहा कि यह उस प्रकार की चीज थी जिसकी मुझे आवश्यकता थी। मैं समझ नहीं पा रहा हूँ कि यह तरीका कैसे नेस्टेड ऑब्जेक्ट्स को हैंडल करेगा। आपके उदाहरण में रिटर्न वैल्यू सेट के लिए लिस्ट है, लेकिन क्या होगा अगर ऑब्जेक्ट पास हुआ था, उसके अंदर तारीखों (एक और खराब डेटाटाइप) के साथ एक सेट था? क्या मुझे डिफ़ॉल्ट विधि के भीतर कुंजियों के माध्यम से ड्रिल करना चाहिए? अनेक अनेक धन्यवाद!
डीकनडेस्पेरडो 17

1
मुझे लगता है कि JSON मॉड्यूल आपके लिए नेस्टेड ऑब्जेक्ट्स को हैंडल करता है। एक बार जब यह सूची वापस मिल जाती है, तो यह उन सूची वस्तुओं पर पुनरावृत्ति कर देगा जो प्रत्येक को एन्कोड करने की कोशिश कर रहे हैं। यदि उनमें से एक तिथि है, तो defaultफ़ंक्शन को फिर से कॉल किया जाएगा, इस बार objदिनांक ऑब्जेक्ट होने के साथ , इसलिए आपको बस इसके लिए परीक्षण करना होगा और दिनांक-प्रतिनिधित्व वापस करना होगा।
१२:०२ पर jterrace

इसलिए डिफ़ॉल्ट विधि कई बार किसी भी एक वस्तु के लिए इसे पारित कर सकती है, क्योंकि यह "कुंजीबद्ध" हो जाने के बाद व्यक्तिगत कुंजी को भी देखेगी?
डीकॉनडेपरेडो

एक ही वस्तु के लिए इसे कई बार नहीं बुलाया जाएगा , लेकिन यह बच्चों में फिर से पैदा हो सकता है। अद्यतन उत्तर देखें।
jterrace

जैसा आपने बताया वैसा ही काम किया। मुझे अभी भी कुछ दोषों का पता लगाना है, लेकिन इसमें से ज्यादातर सामान है जिसे बाहर निकाल दिया जा सकता है। आपके मार्गदर्शन के लिए एक टन धन्यवाद!
डीकॉनडेपरेडो

7

मैंने रेथ हेटिंगर के समाधान को अजगर 3 के लिए अनुकूलित किया ।

यहाँ जो बदल गया है:

  • unicode गायब हो गया
  • माता-पिता के defaultसाथ कॉल को अद्यतन कियाsuper()
  • प्रकार base64को क्रमबद्ध करने के लिए उपयोग करना (क्योंकि ऐसा लगता है कि अजगर 3 में JSON में परिवर्तित नहीं किया जा सकता है)bytesstrbytes
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

केवल शब्दकोश, सूचियाँ और आदिम वस्तु प्रकार (int, string, bool) JSON में उपलब्ध हैं।


5
"आदिम वस्तु प्रकार" पायथन के बारे में बात करते समय कोई मतलब नहीं है। "बिल्ट-इन ऑब्जेक्ट" अधिक समझ में आता है, लेकिन यहां बहुत व्यापक है (शुरुआत के लिए: इसमें dicts, सूचियां और सेट भी शामिल हैं)। (JSON शब्दावली हालांकि अलग हो सकती है।)

स्ट्रिंग संख्या ऑब्जेक्ट
एरेना

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 __ () को एक अप्रत्याशित कीवर्ड तर्क मिला 'iterable_as_array'
atm

आपको सिंपलसन
जैरीब्रिंगर

सिम्पसन को जसन के रूप में आयात करें और फिर json_string = json.dumps (डेटा, iterable_as_array = True) पायथन 3.6 में अच्छी तरह से काम करता है
fraverta

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कुंजी बन गया है । स्पष्ट रूप से एक और, अधिक गहराई से बाधित कुंजी का उपयोग करने के लिए स्वतंत्र महसूस करें।

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