0

I have a custom class like this:

import json

class Config:
    def __init__(self):
        self._field1 = None
        self._field2 = None

    @property
    def field1(self):
        return self._field1

    @field1.setter
    def field1(self, value):
        self._field1 = value

    @property
    def field2(self):
        return self._field2

    @field2.setter
    def field2(self, value):
        self._field2 = value

    def validate(self):
        fields = [attribute for attribute in dir(self) if not attribute.startswith('__') and not callable(getattr(self, attribute))]
        for field in fields:
            if getattr(self, field) == None:
                raise AttributeError("You must set all fields, " + str(field) + " is not set")

    def toJSON(self):
        return json.dumps(self, default=lambda o: vars(o), 
            sort_keys=True, indent=4)

As you can see I try to JSON serialize my class, but it will always include the _ prefix in the json (since vars will return that)

Question 1: How can I get rid of it?

Question 2: I tried to delete the _ prefixes from the class but then I will get a "Error: maximum recursion depth exceeded while calling a Python object" when calling the constructor. Why is that happening?

Later on I might add validation logic to the setters.

Also if you have any suggestions how to refactor my code, please don't keep it to yourself.

Axe319
  • 4,255
  • 3
  • 15
  • 31
Dániel Flach
  • 85
  • 1
  • 13
  • why there is a need to __ ?? is this required ? – Akhilesh_IN Feb 17 '20 at 18:07
  • 2
    Does this answer your question? https://stackoverflow.com/a/31813203/12479639 – Axe319 Feb 17 '20 at 18:19
  • The recursion error comes about because in the constructor, you initialise the variables, thus calling the setter functions. Those functions change the value in the variable, so call the setter function, etc – Ed Ward Feb 17 '20 at 21:54
  • @Akhilesh _ supposed to indicate a private field, since later on I want to add validation logic to the setters and I dont want the instance variables to be modified outside of that. – Dániel Flach Feb 18 '20 at 09:34
  • @EdWard can you tell me why is this happenning here and not with the 'private' fields ? I dont find any documentation on that. – Dániel Flach Feb 18 '20 at 09:52

1 Answers1

0

It might be overkill, but this works:

import json

class _ConfigSerializable:
    __dict__ = {}
    def __init__(self, config):
        for x, y in config.__dict__.items():
            while x.startswith("_"):
                x = x[1:]
            self.__dict__[x] = y

class Config:
    def __init__(self):
        self._field1 = None
        self._field2 = None

    @property
    def field1(self):
        return self._field1

    @field1.setter
    def field1(self, value):
        self._field1 = value

    @property
    def field2(self):
        return self._field2

    @field2.setter
    def field2(self, value):
        self._field2 = value

    def validate(self):
        fields = [attribute for attribute in dir(self) if not attribute.startswith('__') and not callable(getattr(self, attribute))]
        for field in fields:
            if getattr(self, field) == None:
                raise AttributeError("You must set all fields, " + str(field) + " is not set")

    def toJSON(self):
        s = _ConfigSerializable(self)
        return json.dumps(s, default=lambda o: vars(o), 
            sort_keys=True, indent=4)

print(Config().toJSON())

Output:

{
    "field1": null,
    "field2": null
}

Basically, we create a new class which takes the attributes of the config instance, and creates a dictionary, removing the _ at the start of any attribute names. We then serialise that class instance instead.

Ed Ward
  • 2,333
  • 2
  • 10
  • 16