2014-06-30 20 views
32

Làm cách nào để nối tiếp một thành viên Python Enum với JSON, để tôi có thể deserialise kết quả JSON trở lại thành một đối tượng Python?Nối tiếp thành viên Enum với JSON

Ví dụ, mã này:

from enum import Enum  
import json 

class Status(Enum): 
    success = 0 

json.dumps(Status.success) 

kết quả trong các lỗi:

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

Làm thế nào tôi có thể tránh điều đó?

+1

@ZeroPiraeus: Chỉ cần tình cờ gặp vấn đề này. Tôi không biết là tôi đã trả lời rất nhiều! :) –

Trả lời

24

Nếu bạn muốn mã hóa một thành viên enum.Enum tùy ý thành JSON và sau đó giải mã nó thành thành viên enum tương tự (thay vì chỉ đơn giản là thuộc tính của thành viên enum value thuộc tính), bạn có thể làm nên bằng cách viết một JSONEncoder lớp tùy chỉnh, và một chức năng giải mã để vượt qua như là đối số object_hook-json.load() hoặc 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 

Chức năng as_enum dựa trên JSON đã được mã hóa sử dụng EnumEncoder, hoặc một cái gì đó mà cư xử hệt với nó .

Hạn chế đối với thành viên PUBLIC_ENUMS là cần thiết để tránh văn bản độc hại được sử dụng, ví dụ: mã gọi lừa để lưu thông tin cá nhân (ví dụ: khóa bí mật được ứng dụng sử dụng) vào trường cơ sở dữ liệu không liên quan sau đó nó có thể được tiếp xúc (xem http://chat.stackoverflow.com/transcript/message/35999686#35999686).

Ví dụ sử dụng:

>>> 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'} 
+0

hoạt động rất tốt, cảm ơn –

+1

Cảm ơn, Zero! Ví dụ hay. –

+0

Nếu bạn có mã của bạn trong một mô-đun (ví dụ enumencoder.py), bạn phải nhập lớp mà bạn phân tích cú pháp từ JSON thành dict. Ví dụ, trong trường hợp này, bạn phải nhập lớp * Trạng thái * trong mô-đun enumencoder.py. –

20

Câu trả lời đúng tùy thuộc vào những gì bạn định làm với phiên bản được tuần tự hóa.

Nếu bạn định unserialize trở lại vào Python, xem Zero's answer.

Nếu phiên bản serialized của bạn sẽ sang ngôn ngữ khác thì có thể bạn muốn sử dụng một IntEnum thay vào đó, đó là tự động tuần tự như các số nguyên tương ứng:

from enum import IntEnum 
import json 

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

json.dumps(Status.success) 

và điều này trả về:

'0' 
+0

Tôi muốn unserialize trở lại vào Python –

+0

Trong python3.2, Điều này không thực sự trở lại những gì câu trả lời nói. Tôi nhận được: '>>> json.dumps (Status.success)' -> ''Status.success'' – AShelly

+3

@AShelly: Câu hỏi được gắn thẻ với' Python3.4', và câu trả lời này là 3.4+ cụ thể. –

5

tôi thích Zero, Piraeus' câu trả lời, nhưng sửa đổi nó một chút để làm việc với các API cho Amazon Web Services (AWS) được gọi là Boto.

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

sau đó tôi đã thêm phương pháp này để mô hình dữ liệu của tôi:

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

Tôi hy vọng điều này sẽ giúp một ai đó.

Các vấn đề liên quan