JSON के लिए एक Enum सदस्य सीरियल


99

मैं EnumJSON के लिए एक पायथन सदस्य को कैसे अनुक्रमित करूं , ताकि मैं परिणामी JSON को पायथन ऑब्जेक्ट में वापस दे सकूं?

उदाहरण के लिए, यह कोड:

from enum import Enum    
import json

class Status(Enum):
    success = 0

json.dumps(Status.success)

परिणाम में त्रुटि:

TypeError: <Status.success: 0> is not JSON serializable

मैं इससे कैसे बच सकता हूं?

जवाबों:


54

यदि आप enum.EnumJSON के एक मनमाने सदस्य को एनकोड करना चाहते हैं और फिर इसे उसी एनम सदस्य (केवल एनम सदस्य की valueविशेषता के बजाय) के रूप में डिकोड करते हैं , तो आप कस्टम JSONEncoderवर्ग लिखकर और डिकोडिंग फ़ंक्शन को object_hookतर्क के रूप में पारित करने के लिए ऐसा json.load()कर सकते हैं। json.loads():

PUBLIC_ENUMS = {
    'Status': Status,
    # ...
}

class EnumEncoder(json.JSONEncoder):
    def default(self, obj):
        if type(obj) in PUBLIC_ENUMS.values():
            return {"__enum__": str(obj)}
        return json.JSONEncoder.default(self, obj)

def as_enum(d):
    if "__enum__" in d:
        name, member = d["__enum__"].split(".")
        return getattr(PUBLIC_ENUMS[name], member)
    else:
        return d

as_enumफ़ंक्शन JSON पर निर्भर करता EnumEncoderहै जिसका उपयोग करके एन्कोड किया गया है , या ऐसा कुछ जो इसके लिए व्यावहारिक रूप से व्यवहार करता है।

सदस्यों के लिए प्रतिबंध PUBLIC_ENUMSएक दुर्भावनापूर्ण रूप से तैयार किए गए पाठ से बचने के लिए आवश्यक है, उदाहरण के लिए, एक असंबंधित डेटाबेस क्षेत्र में निजी जानकारी (जैसे आवेदन द्वारा उपयोग की जाने वाली एक गुप्त कुंजी) को बचाने में चाल कॉलिंग कोड, जहां से यह तब उजागर हो सकता है। (देखें http://chat.stackoverflow.com/transcript/message/35999686#35999686 )।

उदाहरण उपयोग:

>>> data = {
...     "action": "frobnicate",
...     "status": Status.success
... }
>>> text = json.dumps(data, cls=EnumEncoder)
>>> text
'{"status": {"__enum__": "Status.success"}, "action": "frobnicate"}'
>>> json.loads(text, object_hook=as_enum)
{'status': <Status.success: 0>, 'action': 'frobnicate'}

1
धन्यवाद, शून्य! अच्छा उदाहरण है।
एथन फुरमान

यदि आपके पास एक मॉड्यूल (उदाहरण के लिए enumencoder.py) में आपका कोड है, तो आपको उस वर्ग को आयात करना होगा जिसे आप JSON से तानाशाही के लिए पार्स करते हैं। उदाहरण के लिए, इस स्थिति में, आपको मॉड्यूल enumencoder.py में वर्ग स्थिति आयात करना होगा ।
फ्रांसिस्को मैनुअल गरका बोटेला

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

1
@JaredDeckard मेरी क्षमायाचना, आप सही थे, और मैं गलत था। मैंने उसी हिसाब से जवाब अपडेट किया है। आपके सहयोग के लिए धन्यवाद! यह शैक्षिक (और पीछा करने वाला) रहा है।
जीरो पिरियस

क्या यह विकल्प अधिक उपयुक्त होगा if isinstance(obj, Enum):?
user7440787

126

मुझे पता है कि यह पुराना है लेकिन मुझे लगता है कि इससे लोगों को मदद मिलेगी। मैं बस इस सटीक समस्या से गुज़रा और पता चला कि अगर आप स्ट्रिंग एनम का उपयोग कर रहे हैं, तो अपनी दुश्मनी को strलगभग सभी स्थितियों के लिए अच्छी तरह से काम करने का उपवर्ग घोषित कर रहे हैं :

import json
from enum import Enum

class LogLevel(str, Enum):
    DEBUG = 'DEBUG'
    INFO = 'INFO'

print(LogLevel.DEBUG)
print(json.dumps(LogLevel.DEBUG))
print(json.loads('"DEBUG"'))
print(LogLevel('DEBUG'))

उत्पादन होगा:

LogLevel.DEBUG
"DEBUG"
DEBUG
LogLevel.DEBUG

जैसा कि आप देख सकते हैं, JSON को लोड करना स्ट्रिंग को आउटपुट करता है, DEBUGलेकिन यह आसानी से LogLevel ऑब्जेक्ट में वापस आने योग्य है। एक अच्छा विकल्प अगर आप एक कस्टम JSONEncoder बनाना नहीं चाहते हैं।


1
धन्यवाद। भले ही मैं ज्यादातर कई विरासतों के खिलाफ हूं, लेकिन यह बहुत साफ-सुथरा है और मैं जिस तरह से जा रहा हूं। कोई अतिरिक्त एनकोडर की आवश्यकता नहीं है :)
विनीसियस दांतास

@ कामचोरी, क्या आप अपनी होने वाली समस्या के बारे में विस्तार से बता सकते हैं? मुझे कभी भी एनाम में विशेषता के नाम से भिन्न स्ट्रिंग के मूल्य के साथ कोई समस्या नहीं हुई। क्या मैं आपकी टिप्पणी को गलत समझ रहा हूं?
जस्टिन कार्टर

1
class LogLevel(str, Enum): DEBUG = 'Дебаг' INFO = 'Инфо'इस मामले में enum with strठीक से काम नहीं (
madjardi

1
आप इस ट्रिक को अन्य आधार प्रकारों के साथ भी कर सकते हैं, उदाहरण के लिए (मुझे नहीं पता कि इसे टिप्पणियों में कैसे प्रारूपित किया जाए, लेकिन यह स्पष्ट है: "वर्ग आकार (int, Enum): वर्ग = 1 वृत्त = 2" काम करता है महान एन / ओ एक एनकोडर के लिए की जरूरत है। धन्यवाद, यह एक महान दृष्टिकोण है!
NoCake

एक आकर्षण की तरह काम करता है, धन्यवाद! इसके बजाय उत्तर के रूप में स्वीकार किया जाना चाहिए।
Realfun

72

सही उत्तर इस बात पर निर्भर करता है कि आप क्रमबद्ध संस्करण के साथ क्या करना चाहते हैं।

यदि आप पायथन में वापस जाने के लिए जा रहे हैं, तो ज़ीरो का जवाब देखें

यदि आपका अनुक्रमित संस्करण किसी अन्य भाषा में जा रहा है, तो आप संभवतः IntEnumइसके बजाय का उपयोग करना चाहते हैं , जो स्वचालित रूप से संबंधित पूर्णांक के रूप में क्रमबद्ध है:

from enum import IntEnum
import json

class Status(IntEnum):
    success = 0
    failure = 1

json.dumps(Status.success)

और यह रिटर्न:

'0'

5
@AShelly: प्रश्न के साथ टैग किया गया था Python3.4, और यह उत्तर 3.4+ विशिष्ट है।
एथेन फरमान

2
उत्तम। आप Enum एक स्ट्रिंग है, तो आप का प्रयोग करेंगे EnumMetaबजायIntEnum
bholagabbar

5
@ भोलबागबर: नहीं, आप Enumसंभवतः मिश्रण के साथ प्रयोग करेंगे str-class MyStrEnum(str, Enum): ...
एथन फुरमान

3
@ भोलबागबर, दिलचस्प। आपको उत्तर के रूप में अपना समाधान पोस्ट करना चाहिए।
एतान फुरमान

1
मैं सीधे इनहेरिट करने से EnumMetaबचूंगा, जिसका उद्देश्य केवल एक मेटाक्लस के रूप में था। इसके बजाय, ध्यान दें कि कार्यान्वयन IntEnum एक-लाइनर है और आप उसी के strसाथ प्राप्त कर सकते हैं class StrEnum(str, Enum): ...
युंगचिन

17

पायथन 3.7 में, बस उपयोग कर सकते हैं json.dumps(enum_obj, default=str)


अच्छा लग रहा है, लेकिन यह nameदूत स्ट्रिंग में Enum की लिखेंगे । बेहतर तरीका होगा valueकि आप एनम का इस्तेमाल करें ।
eNca

Enum मान द्वारा उपयोग किया जा सकता हैjson.dumps(enum_obj, default=lambda x: x.value)
eNca

10

मुझे ज़ीरो पीरियस का उत्तर पसंद आया, लेकिन अमेज़ॅन वेब सर्विसेज (एडब्ल्यूएस) के लिए एपीआई के साथ काम करने के लिए इसे थोड़ा संशोधित किया।

class EnumEncoder(json.JSONEncoder):
    def default(self, obj):
        if isinstance(obj, Enum):
            return obj.name
        return json.JSONEncoder.default(self, obj)

मैंने फिर इस विधि को अपने डेटा मॉडल में जोड़ा:

    def ToJson(self) -> str:
        return json.dumps(self.__dict__, cls=EnumEncoder, indent=1, sort_keys=True)

मुझे उम्मीद है इससे किसी को सहायता मिलेगी।


आपको ToJsonअपने डेटा मॉडल में जोड़ने की आवश्यकता क्यों है ?
यू चेन

2

अगर आप jsonpickleसबसे आसान तरीका इस्तेमाल कर रहे हैं तो नीचे देखना चाहिए।

from enum import Enum
import jsonpickle


@jsonpickle.handlers.register(Enum, base=True)
class EnumHandler(jsonpickle.handlers.BaseHandler):

    def flatten(self, obj, data):
        return obj.value  # Convert to json friendly format


if __name__ == '__main__':
    class Status(Enum):
        success = 0
        error = 1

    class SimpleClass:
        pass

    simple_class = SimpleClass()
    simple_class.status = Status.success

    json = jsonpickle.encode(simple_class, unpicklable=False)
    print(json)

Json क्रमांकन के बाद आप के {"status": 0}बजाय की उम्मीद होगी

{"status": {"__objclass__": {"py/type": "__main__.Status"}, "_name_": "success", "_value_": 0}}

-2

यह मेरे लिए काम किया:

class Status(Enum):
    success = 0

    def __json__(self):
        return self.value

कुछ और बदलना नहीं था। जाहिर है, आपको केवल इसका मूल्य ही मिलेगा और अगर आप बाद में इस मूल्य को क्रमबद्ध मान को एनम में बदलना चाहते हैं तो आपको कुछ अन्य काम करने की आवश्यकता होगी।


2
मुझे उस मैजिक विधि का वर्णन करने वाले डॉक्स में कुछ भी दिखाई नहीं देता है । क्या आप कुछ अन्य JSON लाइब्रेरी का उपयोग कर रहे हैं, या क्या आपके पास JSONEncoderकहीं कस्टम है?
0x5453
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.