Serialize Python Objects in JSON

Asked

Viewed 267 times

0

Does anyone know how to serialize in YAML or JSON a class that contains attributes that contain other classes?

Class player has the Spellbook attribute (class that returns a magic list) and the Backpack attribute (class that returns another list that contains the items, etc.) that are subclasses of a list.

class Player:
    def __init__(self,name='', *args, **kwargs):
        super().__init__(*args, **kwargs)
        self._name = name
        self.spellbook = Spellbook(unit=self)
        self.base_damage = 0
        self.backpack = Backpack()
        self.level = 0
        self.stats = {'strength': 1, 'inteligence': 1}
        self.stats_points = 5
        self._up_experience = 0
        self._experience = 0
        self._max_health = 0
        self._health = 0
        self.max_mana = 0
        self._mana = 0
        self.base_mana_regeneration = 0
        self._mana_regeneration = 0
        self.potions = 4
        self._defense = 0
        self.base_defense = 0
        self.live_status = True
        self._gold = 0
        self._score = 0
        self.equipped_items = {
            'weapon': None,
            'shield': None,
            'head': None,
            'chest': None,
            'legs': None,
            'boots': None,
        }
        self._level_up()

Always when it will serialize it returns this error:

TypeError: Object of type Heal is not JSON serializable

In such case the error is talking about the Spell(Heal) you have in Spellbook, but it shows the same problem regarding the items.

This post here has a guy who gives an example similar to what I need, but the code didn’t work on me, it’s in an infinite loop.

Serialize Object to JSON

@Edit1

Function that returns the attributes it contains in the player.

def save_data(player):
    data = {}
    for attr in player.__dict__:
        data[attr] = player.__dict__[attr]
    return data

Function that saves the file.

    def save_char(self, char):
        from game.units import save_data
        json.dump(save_data(char), open(f'saves/{char.name}.json', 'w'))
  • How do you have it serialized? This code has to be in the question. json.dumps which is the normal way to serialize things in JSON does not work for class instances.

  • It’s exactly the json.dumps I’m using.

  • 1

    (know that you can press the button {} to format the code here and preserve the indentation, right? Or delimite the code with three ``` - but fix the indentation - indentation in Python is not only "cute" - its above code snippets are all syntax errors, and that takes away people who might want to answer)

  • I still don’t know the platform commands well. I hadn’t even noticed that I was out of identation. Thank you!

1 answer

2


TL;DR: Unless you need to pass the data to a program written in another language, or edit manually as a text file, use pickle instead of json to save object states.

Then the excerpt crucial that was missing from the original version of this question is:

def save_data(player):
    data = {}
    for attr in player.__dict__:
        data[attr] = player.__dict__[attr]
    return data

Without it it seemed like you were using something to directly serialize a player class object - it’s important to understand the information you need to pass when asking a question - no one has a crystal ball to know what you’re trying to serialize.

So - what you pass up to json.dump is a dictionary - the standard JSON serializer knows how to turn dictionaries into JSON. But he doesn’t know how to turn arbitrary classes into JSON.

These are two paths to follow to serialize all fields as JSON: either you make the above function recursive, and each time you find an object other than a list or a dictionary, call the function itself save_data passing the sub-object (of course it is better to give a more appropriate name for the function) - or, you customize a JSON serializer that understands common classes, and serialize the __dict__ instances. This second path requires the creation of a subclass of json.JSONEncoder.

Either of the above two methods have an obvious disadvantage - to "rehydrate" the objects, when reading JSON, you will have dictionaries, not objects of your class. To recreate the classes correctly it is necessary to create a function that will be passed as the parameter object_hook for json.load, or a custom Decoder.

On the other hand - if you don’t need your JSON files to be editable directly in a text editor - that is, your game that will always save and read the files, there is not the slightest need to use JSON for what you want -

Simply save the objects of your program using pickle instead of JSON. The difference is that pickle can save and restore arbitrary Python objects, and not just strings, numbers, booleans, dictionaries and lists, which is a limitation of JSON.

In that case you don’t even need the code that’s in this save_data - simply use the pickle the same way you use the json module, but pass its instance directly:

import pickle
...
pickle.dump(player, open(f'saves/{char.name}.pickle', 'wb'))
...
player  = pickle.load(open(open(f'saves/{char.name}.pickle', 'rb'))

(note that the pickle requires the file to be opened in binary mode, with the letter "b" after "r" or "w")

Browser other questions tagged

You are not signed in. Login or sign up in order to post.