জেএসএনে এনাম সদস্যকে সিরিয়ালাইজ করা


96

আমি কীভাবে পাইথনের Enumসদস্যকে জেএসএন-তে সিরিয়াল করব , যাতে ফলস্বরূপ JSON কে পাইথন অবজেক্টে ফিরিয়ে আনতে পারি?

উদাহরণস্বরূপ, এই কোড:

from enum import Enum    
import json

class Status(Enum):
    success = 0

json.dumps(Status.success)

ত্রুটির ফলাফল:

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

আমি কীভাবে এড়াতে পারি?

উত্তর:


52

আপনি যদি 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প্রয়োজনীয়ভাবে দূষিতভাবে তৈরি করা পাঠ্য এড়ানো প্রয়োজন, উদাহরণস্বরূপ, ব্যক্তিগত তথ্য সংরক্ষণের ক্ষেত্রে কলিং কোডটি (উদাহরণস্বরূপ অ্যাপ্লিকেশন দ্বারা ব্যবহৃত একটি গোপনীয় চাবি) কোনও সম্পর্কযুক্ত ডেটাবেস ক্ষেত্রে ব্যবহার করা যেতে পারে, সেখান থেকে এটি প্রকাশ করা যেতে পারে from ( 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'}

4
ধন্যবাদ, জিরো! চমৎকার উদাহরণ।
ইথান ফুরম্যান

যদি আপনার কোডটি একটি মডিউলে থাকে (উদাহরণস্বরূপ, enumencoder.py), আপনার অবশ্যই JSON থেকে ডেকের জন্য পার্স করা ক্লাসটি আমদানি করতে হবে। উদাহরণস্বরূপ, এই ক্ষেত্রে, আপনাকে অবশ্যই মডিউল enumencoder.py এ শ্রেণি স্থিতি আমদানি করতে হবে ।
ফ্রান্সিসকো ম্যানুয়েল গারকা বোটেলা

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

4
@ জার্ডডেকার্ড আমার ক্ষমা চেয়েছি, আপনি ঠিকই বলেছিলেন এবং আমি ভুল ছিলাম। আমি সেই অনুযায়ী উত্তর আপডেট করেছি। আপনার ইনপুট জন্য ধন্যবাদ! এটি শিক্ষামূলক (এবং অনুশাসন) হয়েছে।
জিরো পাইরেয়াস

এই বিকল্পটি আরও উপযুক্ত হতে পারে if isinstance(obj, Enum):?
ব্যবহারকারী 7440787

114

আমি জানি এটি পুরানো তবে আমি অনুভব করি এটি লোকদের সহায়তা করবে। আমি ঠিক এই সঠিক সমস্যার মধ্য দিয়ে গিয়েছিলাম এবং আবিষ্কার করেছি যে আপনি যদি স্ট্রিং এনামগুলি ব্যবহার করছেন, আপনার এনামগুলিকে 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

আপনি দেখতে পাচ্ছেন, DEBUGজেএসএন লোড করা স্ট্রিংটিকে আউটপুট দেয় তবে এটি সহজেই লগলভিল অবজেক্টে ফিরে যেতে পারে। আপনি যদি পছন্দসই JSONEncoder তৈরি করতে না চান তবে একটি ভাল বিকল্প।


4
ধন্যবাদ যদিও আমি বেশিরভাগ ক্ষেত্রে একাধিক উত্তরাধিকারের বিরুদ্ধে আছি, এটি বেশ ঝরঝরে এবং আমি সেই পথেই চলেছি। কোনও অতিরিক্ত এনকোডার প্রয়োজন নেই :)
ভিনিসিয়াস ড্যান্টাস

@ মাদজার্দি, আপনি যে সমস্যার মুখোমুখি হচ্ছেন তা কি আপনি বিশদভাবে বলতে পারবেন? এনামের গুণাবলীর নামের চেয়ে স্ট্রিংয়ের মান আলাদা হওয়ার ক্ষেত্রে আমার কখনও সমস্যা হয়নি। আমি কি তোমার মন্তব্যে ভুল বুঝছি?
জাস্টিন কার্টার

4
class LogLevel(str, Enum): DEBUG = 'Дебаг' INFO = 'Инфо'এই ক্ষেত্রে enum with strকোন কাজ সঠিকভাবে (
madjardi

4
আপনি অন্যান্য বেস ধরণের সাথেও এই কৌশলটি করতে পারেন, উদাহরণস্বরূপ (মন্তব্যগুলিতে এটি কীভাবে ফর্ম্যাট করতে হয় তা আমি জানি না, তবে বক্তব্যটি স্পষ্ট: "শ্রেণীর আকারগুলি (ইনট, এনুম): বর্গ = 1 বৃত্ত = 2" কাজ করে দুর্দান্ত
ডাব্লু

71

সঠিক উত্তরটি সিরিয়ালযুক্ত সংস্করণ দিয়ে আপনি কী করতে চান তার উপর নির্ভর করে।

আপনি যদি পাইথনে ফিরে অনিয়ন্ত্রিত হতে চলেছেন তবে জিরোর উত্তর দেখুন

যদি আপনার সিরিয়ালযুক্ত সংস্করণটি অন্য কোনও ভাষাতে চলেছে তবে আপনি সম্ভবত IntEnumতার পরিবর্তে কোনও ব্যবহার করতে চান যা স্বয়ংক্রিয়ভাবে সম্পর্কিত পূর্ণসংখ্যা হিসাবে সিরিয়ালযুক্ত:

from enum import IntEnum
import json

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

json.dumps(Status.success)

এবং এটি ফিরে আসে:

'0'

4
@ শ্যাশলি: প্রশ্নটি ট্যাগ করা হয়েছিল Python3.4এবং এই উত্তরটি নির্দিষ্টভাবে 3.4+ is
ইথান ফুরম্যান 15

4
পারফেক্ট আপনি যদি EnumMetaIntEnum
এনুম

4
@ ভোলগাব্বার: না, আপনি Enumসম্ভবত একটি strমিশ্রণ দিয়ে ব্যবহার করবেন -class MyStrEnum(str, Enum): ...
এথান ফুরম্যান

4
@ ভোলগাব্বার, আকর্ষণীয়। উত্তর হিসাবে আপনার সমাধান পোস্ট করা উচিত।
ইথান ফুরম্যান

4
আমি সরাসরি উত্তরাধিকার সূত্রে EnumMetaএড়াব, যা কেবলমাত্র একটি মেটাক্লাস হিসাবে তৈরি হয়েছিল। পরিবর্তে, নোট বাস্তবায়ন যে IntEnum একটি এক মাছ ধরার নৌকা হয় এবং আপনার জন্য একই অর্জন করতে পারেন strসঙ্গে class StrEnum(str, Enum): ...
ইয়ংচিন

15

পাইথন ৩.7-এ কেবল ব্যবহার করতে পারেন json.dumps(enum_obj, default=str)


দেখতে দুর্দান্ত nameলাগছে তবে এটি জসন স্ট্রিংয়ে এনাম লিখবে । valueএনাম ব্যবহার করা আরও ভাল উপায় ।
ইএনকা

json.dumps(enum_obj, default=lambda x: x.value)
এনাম

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)

জসন সিরিয়ালাইজেশনের পরে আপনার {"status": 0}পরিবর্তে প্রত্যাশা মতো হবে

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

-1

এটি আমার পক্ষে কাজ করেছে:

class Status(Enum):
    success = 0

    def __json__(self):
        return self.value

অন্য কিছু পরিবর্তন করতে হবে না। স্পষ্টতই, আপনি কেবল এর থেকে মানটি পাবেন এবং আপনি যদি সিরিয়ালযুক্ত মানটি পরে এনামে রূপান্তর করতে চান তবে অন্য কিছু কাজ করতে হবে।


4
আমি সেই যাদু পদ্ধতিটি বর্ণনা করে ডক্সে কিছুই দেখতে পাচ্ছি না । আপনি কি অন্য কিছু জেএসওএন লাইব্রেরি ব্যবহার করছেন, বা আপনার JSONEncoderকোথাও একটি প্রথা আছে?
0x5453
আমাদের সাইট ব্যবহার করে, আপনি স্বীকার করেছেন যে আপনি আমাদের কুকি নীতি এবং গোপনীয়তা নীতিটি পড়েছেন এবং বুঝতে পেরেছেন ।
Licensed under cc by-sa 3.0 with attribution required.