1

I have an object Entry with following fields as id, scene_info and rating. As can be seen, the object has attributes that are types to other classes Scene and Item. I want to convert this object to dictionary.

Entry(id=None, scene_info=Scene(Recipes=[Item(ID='rec.chicky-nuggies', SpawnerIdx=0), Item(ID='rec.impossible-burger', SpawnerIdx=1)], Decor=[Item(ID='dec.plate-large-orange', SpawnerIdx=2), Item(ID='dec.plate-small-green', SpawnerIdx=3)]), rating=None)
(Pdb) vars(self)
{'id': None, 'scene_info': Scene(Recipes=[Item(ID='rec.chicky-nuggies', SpawnerIndex=0), Item(ID='rec.impossible-burger', SpawnerIdx=1)], Decor=[Item(ID='dec.plate-large-orange', SpawnerIdx=2), Item(ID='dec.plate-small-green', SpawnerIdx=3)]), 'rating': None}

EXPECTED RESULT

{'id': None, 'scene_info':{'Recipes': [{'ID': 'rec.chicky-nuggies', 'SpawnerIdx': 0}, {'ID': 'rec.impossible-burger', 'SpawnerIdx': 1}], 'Decor': [{'ID': 'dec.plate-large-orange', 'SpawnerIndex': 2}, {'ID': 'dec.plate-small-green', 'SpawnerIdx': 3}]}, 'rating': None}

I tried vars and they only convert outer object to dict but not inner object. How can I convert the nested ones?

1
  • Pydantic models have model.dict(...) method. Commented Sep 15, 2020 at 3:26

4 Answers 4

1

I usually do it this way:

class Bar:
    # child class
    # some init code...

    def encode(self):
        return vars(self)

class Foo:
    # parent class
    # some init code...

    def encode(self):
        return vars(self)

    def to_json(self, indent=None):
        return json.dumps(self, default=lambda o: o.encode(), indent=indent)

to_json() will give you a json string for the class and its nested objects if they are simple enough, you can also use marshmallow to do this with more control. You could just do return json.dumps(self, default=lambda o: vars(o), indent=indent) in the parent class and not have the encode() method but using the encode method allows you to customize the output.

Here is some random, silly code to show how it might be used and the output:

import json


class Ingredient:
    def __init__(self, name, cost=0):
        self.name = name
        self.cost = cost

    def encode(self):
        return vars(self)


class Recipe:
    def __init__(self, name, prep_time=0, cook_time=0, ingredients=None,
                 instructions=None):
        self.name = name
        self.prep_time = prep_time
        self.cook_time = cook_time
        self.ingredients = ingredients or []
        self.instructions = instructions or {}

    def encode(self):
        return vars(self)

    def to_json(self, indent=None):
        return json.dumps(self, default=lambda o: o.encode(), indent=indent)


lettuce = Ingredient('Lettuce', 1.3)
tomato = Ingredient('Tomato', 5.2)

salad = Recipe('Salad', prep_time=5, cook_time=0)

salad.ingredients = [
    lettuce,
    tomato
]

salad.instructions = {
    'Step 1': 'Get the ingredients out',
    'Step 2': 'Mix tem together',
    'Step 3': 'Eat' 
}

print(salad.to_json(4))

Output:

{
    "name": "Salad",
    "prep_time": 5,
    "cook_time": 0,
    "ingredients": [
        {
            "name": "Lettuce",
            "cost": 1.3
        },
        {
            "name": "Tomato",
            "cost": 5.2
        }
    ],
    "instructions": {
        "Step 1": "Get the ingredients out",
        "Step 2": "Mix tem together",
        "Step 3": "Eat"
    }
}
Sign up to request clarification or add additional context in comments.

Comments

1

The prefered way to go would be using modifing class definition as stated by Tenacious B, but if you want a fast solution you can use the recursive function stated below.

def class2dict(instance, built_dict={}):
    if not hasattr(instance, "__dict__"):
        return instance
    new_subdic = vars(instance)
    for key, value in new_subdic.items():
        new_subdic[key] = class2dict(value)
    return new_subdic

Example:

# Class definitions
class Scene:
    def __init__(self, time_dur, tag):
        self.time_dur = time_dur
        self.tag = tag


class Movie:
    def __init__(self, scene1, scene2):
        self.scene1 = scene1
        self.scene2 = scene2


class Entry:
    def __init__(self, movie):
        self.movie = movie
In [2]: entry = Entry(Movie(Scene('1 minute', 'action'), Scene('2 hours', 'comedy')))                                                                                                                                                                                       
In [3]: class2dict(entry)                                                                                                             
Out[3]:                                                                                                                          
{'movie': {
    'scene1': {'time_dur': '1 minute', 'tag': 'action'},                                                                         
    'scene2': {'time_dur': '2 hours', 'tag': 'comedy'}}
}     
  

                                                                              

Comments

1

You can use the Pydantic model's Entry.json(), this will convert everything including nested models to a string which can then be converted back to a dictionary by using something like json.loads()

Comments

0

For the class types (Entry\Scene\Item), you can create a function the returns the arguments as a dictionary.

Try this code:

def getargs(**kwargs):
   return kwargs  # already a dictionary

Entry = Scene = Item = getargs  # all functions do same thing

x = Entry(id=None, scene_info=Scene(Recipes=[Item(ID='rec.chicky-nuggies', SpawnerIdx=0), Item(ID='rec.impossible-burger', SpawnerIdx=1)], Decor=[Item(ID='dec.plate-large-orange', SpawnerIdx=2), Item(ID='dec.plate-small-green', SpawnerIdx=3)]), rating=None)

print(x)

Output

{'id': None, 'scene_info': {'Recipes': [{'ID': 'rec.chicky-nuggies', 'SpawnerIdx': 0}, {'ID': 'rec.impossible-burger', 'SpawnerIdx': 1}], 'Decor': [{'ID': 'dec.plate-large-orange', 'SpawnerIdx': 2}, {'ID': 'dec.plate-small-green', 'SpawnerIdx': 3}]}, 'rating': None}

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.