7

I would like to create a class, which has an enumeration as an attribute. This enumeration should have a string representation that shows up as human-readable value when dumping the instance of the class that uses the enum attribute as a JSON string. In the minimal working example below, I created three enumerations in three different ways.

After the deserialization, each attribute shows us that it comes from an enumeration except the enumeration with a string representation. It is just a string.

If it is not possible to realize such a structure, I would like to know why.

Requirements

If you would like to test it, you have to install jsons and attrs with

pip install attrs jsons

Minimal working example

Here you see a minimal working example.

import jsons
import attr

from enum import Enum

# ----------------------------------------------------
# create enumeration with the help of a dictionary

fruits = {
    "PINEAPPLE": "PINEAPPLE",
    "APPLE": "APPLE",
    "ORANGE": "ORANGE",
    "BANANA": "BANANA",
}

Fruit = Enum("FRUITS", fruits)

# ----------------------------------------------------
# create a classical enumeration


class Nut(Enum):

    PEANUT = 1
    HAZELNUT = 2
    CASHEW = 3
    WALNUT = 4


# ----------------------------------------------------
# create enumeration with a string representation


class Vegetable(str, Enum):

    BROCCOLI = "BROCCOLI"
    CUCUMBER = "CUCUMBER"
    POTATO = "POTATO"
    ONION = "ONION"


# ----------------------------------------------------
# create a class which uses the enumerations


@attr.s(auto_attribs=True, kw_only=True)
class Order(jsons.JsonSerializable):
    fruit: Fruit
    nut: Nut
    vegetable: Vegetable


# ----------------------------------------------------
# initialize an order object, serialize and deserialize it

order = Order(fruit=Fruit.APPLE, nut=Nut.PEANUT, vegetable=Vegetable.CUCUMBER)

json_string: str = Order.dumps(order)

order_deserialised: Order = Order.loads(json_string)

Structure of the order and order_deserialised variable:

order:              Order(fruit=<FRUITS.APPLE: 'APPLE'>, nut=<Nut.PEANUT: 1>, vegetable=<Vegetable.CUCUMBER: 'CUCUMBER'>)

order_deserialised: Order(fruit=<FRUITS.APPLE: 'APPLE'>, nut=<Nut.PEANUT: 1>, vegetable='CUCUMBER')

As you can see, the order_deserialised shows the vegetable as a string and not an enumeration.

Pratik Patel
  • 5,995
  • 1
  • 18
  • 39
unlimitedfox
  • 386
  • 4
  • 8
  • Why are you inheriting from `str`??? `class Vegetable(str, Enum):`? This is likely throwing off the `attrs`/`jsons` libraries, somewhere I suspect, there is some check `isinstance(x, str)`, and it isn't handling `str` subtypes.... "create enumeration with a string representation" That isn't a reasonable way of doing this. Just write a `__str__` and a `__repr__` method. Inheriting from `str` seems to be your issue. Your enumeration *is a string*, that is what inheriting from `str` does, so it's not surprising that it is being treated as one. – juanpa.arrivillaga Dec 17 '20 at 11:28
  • Fundamentally, it isn't clear what you mean by "create an enumeration with a string reprsentation", *what is it you are tryin to achieve by inheriting from `str`*? – juanpa.arrivillaga Dec 17 '20 at 11:36
  • Yeah you are both right. I saw this structure on [another stack overflow entry](https://stackoverflow.com/questions/58608361/string-based-enum-in-python/58608362#58608362) I stick to the dictionary ansatz. Thanks for your help =) – unlimitedfox Dec 18 '20 at 21:18
  • Or of course, you can just *not* inherit from `str`, so `class Vegetable(Enum): ...` – juanpa.arrivillaga Dec 18 '20 at 21:21
  • This enumeration is an attribute of a class which will be serialised into a JSON file. There I would like to see the value and not only the number. Or is there also another option to achieve this? – unlimitedfox Dec 18 '20 at 21:29
  • 2
    What do you mean, "the value, not only the number", *what number*? In any case, your library almost certainly allows you to control the serialization any way you want. Having an `enum` type inherit from `str` **defeats the whole purpose of `enum`** in the same way as having it inherit from `int`. I re-opened the question, because I don't think the duplicate was a great fit. – juanpa.arrivillaga Dec 19 '20 at 20:59

1 Answers1

2

Maybe you already know this, but I am answering for future readers.

This problem was fixed in JSONS 1.6.1, as you can see in the following example.

import enum
import jsons

class Vegetable(str, enum.Enum):
    BROCCOLI = "BROCCOLI"

print(jsons.loads(jsons.dumps(Vegetable.BROCCOLI), Vegetable))
# This will output "Vegetable.BROCCOLI" with JSONS 1.6.1 or later

And deriving a class like class Vegetable(str, Enum) is perfectly fine, unlike what others commented on the question. A similar idea was added as the StrEnum in Python 3.11.

relent95
  • 3,703
  • 1
  • 14
  • 17