2011-11-22 18 views
97

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

मैं json एनकोड करने के लिए इस परिणाम set आवश्यकता है, लेकिन गुजर भी एक खाली setjson.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__ पर अनुवाद करती हैं, लेकिन वे स्वयं भी उन गुणों के लिए मूल्य रख सकते हैं जो जेसन एन्कोडर में मूल प्रकारों के लिए अपात्र हो सकते हैं।

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

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

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

+2

क्यों 'dict's? मुझे लगता है कि आप सेट से सिर्फ 'सूची' बनाना चाहते हैं और फिर इसे एन्कोडर में पास करना चाहते हैं ... उदाहरण: 'एनकोड (सूची (माइसेट))' – Constantinius

+1

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

+0

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

उत्तर

78

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

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

एक अधिक परिष्कृत समाधान एक कस्टम प्रकार का निर्माण करना है जो अन्य मूल JSON प्रकारों के साथ मिलकर बन सकता है।

>>> 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')] 
: इससे आप नेस्टेड संरचनाओं कि सूचियों, सेट, dicts, दशमलव, datetime वस्तुओं, आदि .: शामिल की दुकान

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, और सेट संभाल कर सकते हैं एक नमूना सत्र है की सुविधा देता है

वैकल्पिक रूप से, YAML, Twisted Jelly, या पायथन के pickle module जैसे अधिक सामान्य उद्देश्य क्रमिकरण तकनीक का उपयोग करना उपयोगी हो सकता है। ये प्रत्येक डेटाटाइप की एक बड़ी श्रृंखला का समर्थन करते हैं।

+0

अपने नमूना सत्र में, क्या PythonSetEncoder PythonObjectEncoder होना चाहिए? –

+0

बस सुनिश्चित करें कि अविश्वसनीय इनपुट पर इसका उपयोग न करें, क्योंकि [अचार गलत या दुर्भावनापूर्ण रूप से निर्मित डेटा के विरुद्ध सुरक्षित होने का इरादा नहीं है] (http://docs.python.org/library/pickle.html), जबकि JSON है (अचार के साथ अनुकूलित जब तक)। –

+7

यह पहला है कि मैंने सुना है कि वाईएएमएल जेएसओएन की तुलना में अधिक सामान्य उद्देश्य है ... o_O –

3

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

+3

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

+0

स्ट्रिंग संख्या वस्तु सरणी सच झूठी अशक्त –

67

आप एक कस्टम एन्कोडर बना सकते हैं जो देता है जब यह 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)} की तरह कुछ काम कर सकता है।

सचित्र नेस्टेड प्रकार के

, serializing पर विचार करें:

>>> 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 परिणाम वापस ले जाएगा और रिकर्सिवली पर serializer फोन इसके बच्चे कई प्रकार के लिए एक कस्टम serializer जोड़ने के लिए, आप यह कर सकते हैं:

>>> 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"]' 
+0

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

+1

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

+0

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

3

आप केवल सेट, नहीं सामान्य अजगर वस्तुओं, सांकेतिक शब्दों में बदलना करने की जरूरत है और यह आसानी से मानव पठनीय रखने के लिए, रेमंड Hettinger के का एक सरलीकृत संस्करण चाहते हैं जवाब के लिए इस्तेमाल किया जा सकता है:

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 
2

मैं अजगर को Raymond Hettinger's solution अनुकूलित 3.

यहाँ क्या बदल गया है:

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

[इस उत्तर] के अंत में दिखाया गया कोड (http://stackoverflow.com/a/18561055/355230) किसी संबंधित प्रश्न पर [केवल] डिकोडिंग और बाइट्स ऑब्जेक्ट 'json.dumps () '' latin1'' से 'लौटता है,' बेस 64' सामान को छोड़कर जो जरूरी नहीं है। – martineau

संबंधित मुद्दे