0

What is the most compact way to serialize an object that contains other objects or even nested collections of other objects in python?

More info:

In my project, I have collections of instances of my own classes, and each some of these classes again contain collections of instances of some of my other classes.

I need to transfer some of these collections through a REST api using json files.

We can use an example like below.

Example Code:

from typing import List, Dict


class Organization:
    def __init__(self, name):
        self.name = name
        self.employees = []  # type: List[Person]


class Person:
    def __init__(self, name: str):
        self.name = name
        self.qualification = ""
        self.pets = []  # type: List[Pet]


class Pet:
    def __init__(self, name: str, species: str):
        self.species = species
        self.name = name


if __name__ == "__main__":
    pets_dict = {"Simba": "cat", "Winnie": "bear", "Tigger": "bear", "Drogon": "dragon",
                "Rhaegal": "dragon", "Viserion":"dragon",  "Ghost": "wolf"}
    people_dict = ["Walt", "Christopher", "Dany", "Jon"]
    pets_obj = {p: Pet(p, pets_dict[p]) for p in pets_dict}  # type: Dict[str, Pet]
    people_obj = {p: Person(p) for p in people_dict}  # type: Dict[str, Person]
    people_obj["Walt"].pets = [pets_obj["Simba"],]
    people_obj["Christopher"].pets = [pets_obj["Winnie"], pets_obj["Tigger"]]
    people_obj["Dany"].pets = [pets_obj["Drogon"], pets_obj["Rhaegal"], pets_obj["Viserion"]]
    people_obj["Jon"].pets = [pets_obj["Ghost"], ]
    organization = Organization("Stories")
    organization.employees = list(people_obj.values())

    print(vars(organization))

Output:

{'name': 'Stories', 'employees': [<__main__.Person object at 0x00000000025DFDD8>, <__main__.Person object at 0x00000000025E6630>,  <__main__.Person object at 0x00000000025E6AC8>, <__main__.Person object at 0x00000000025E6B38>]}

As we can see, only the highest level of the object has been serialized, while the objects inside any of its collection keep their type.

Solutions Found So Far:

  1. There is this question-answer. It contains 2 suggestions: The first is for simple cases only, by using dumps(organization.__dict__). This does not work in the example case. The second method is to write an encoding class, which is similar to the solution (2) below. It's not a bad solution, but see my "Why I think there should be a better way" below.

  2. Use json.dump (or dumps) and include a serializer for each custom class.

    def my_serializer(obj):
        if type(obj) in (Organization, Person, Pet):
            return vars(obj)
        else:
            return obj
    

then do

    print(dumps(organization, default=my_serializer))
  1. Write a self.export method for each class, which will serialize itself, and for any collections of custom classes it contains, call the export method of that class when serializing.

Why I think there should be a better way:

Essentially I am looking for a deep vars (like there is a deepcopy). I can imagine that there are many who have wanted such a thing, so I expect a simple and clear solution exists. I just couldn't Google my way to it, so I turn to the community.

JoshG
  • 6,472
  • 2
  • 38
  • 61
levraininjaneer
  • 1,157
  • 2
  • 18
  • 38

1 Answers1

1

Option 2 - passing a serialiser to json.dumps - is the right way to do this. You provide the serialiser, json.dumps handles recursing through the object graph.

There's no value in deep_vars unless you want a dictionary of your object graph, but you don't want a dictionary, you just the json.

snakecharmerb
  • 47,570
  • 11
  • 100
  • 153