0

I would like to serialize users into json. User has only private fields.

user.py

class User:
    def __init__(self, id, name):
        self. set_id(id)
        self.set_name(name)

    def set_id(self, id):
        self.__id = id

    def set_name(self, name):
        self.__name = name

json_encoder.py, without it json.dumps does not work.

from json import JSONEncoder


class JsonEncoder(JSONEncoder):
    def default(self, o):
        return o.__dict__

user_app.py

import json
from json_encoder import JsonEncoder
from user import User

def main():
    users = build_users(names) # a function that returns list of users
    users_json = json.dumps(users, indent=4, sort_keys=True, cls=JsonEncoder)
    print(users_json)


if __name__ == '__main__':
    main()

It prints

[
    {
        "_User__id": 0,
        "_User__name": "Alex"
    },
    {
        "_User__id": 1,
        "_User__name": "John"
    }
]

Which is almost good. I would like, however, to have private fields as field names - without class name prefix.

[
    {
        "id": 0,
        "name": "Alex"
    }
]

Is there annotation, or should I write custom helper to customize json output.

By the way, is it a good practice to make private fields in python? I come from Java world, where we do prefer private fields.

Update

How to make a class JSON serializable partially answers my question. How to serialize it with beautiful properties names, see example of my output, I still have to learn more. I did not know how customize JsonEncoder. I posted my answer.

Yan Khonski
  • 12,225
  • 15
  • 76
  • 114
  • 1
    Possible duplicate of [How to make a class JSON serializable](https://stackoverflow.com/questions/3768895/how-to-make-a-class-json-serializable) – rdas Oct 05 '19 at 19:31
  • 2
    Since you already have a custom encoder, why not put the custom field names there? – rdas Oct 05 '19 at 19:32
  • 1
    You'd have to write a custom JSONEncoder - `_User__id` is the *actual name* of the attribute, name mangling is how Python implements privacy. Note that it isn't particularly Pythonic to use setters/getters at all, just access the attributes directly. (If you do find a need to execute code on set/get, you can add that after the fact via `@property`.) – jasonharper Oct 05 '19 at 19:32
  • @rdas how to put custom field names? – Yan Khonski Oct 05 '19 at 19:43
  • Instead of returning `o.__dict__`, build a dictionary from the `id` & `name` from `o`. You'll need getters for `id` & `name` – rdas Oct 05 '19 at 19:45

1 Answers1

0

Thanks to jasonharper So far, I found example about properties and setters Now I know how to do encapsulation in python.

user.py

class User:
    def __init__(self, id, name):
        self.id = id
        self.name = name

    @property
    def id(self):
        return self.__id

    @id.setter
    def id(self, id):
        self.__id = id

    @property
    def name(self):
        return self.__name

    @name.setter
    def name(self, name):
        self.__name = name

Next, I need to serialize it into JSON.

json_encoder.py

from json import JSONEncoder


def beautify_key(str):
    index = str.index('__')
    if index <= 0:
        return str

    return str[index + 2:]


class JsonEncoder(JSONEncoder):

    def default(self, o):
        return {beautify_key(k): v for k, v in vars(o).items()}

Now the output is correct for me.

[
    {
        "id": 0,
        "name": "Alex"
    },
    {
        "id": 1,
        "name": "John"
    }
]
Yan Khonski
  • 12,225
  • 15
  • 76
  • 114