简体   繁体   中英

How to convert nested object to nested dictionary in python

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?

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"
    }
}

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}

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'}}
}     
  

                                                                              

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()

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM