Adrian
Adrian

Reputation: 163

Modelling 'has a' relationship

I'm attempting to model this JSON describing a Car :

{
colour : "green"
specs : {
brakes : "good brakes"
}
}

A car 'has a' relationship with specs. Here is how I implement:

import json

class Car :
    def __init__(self , colour, specs):
        self.colour = colour
        self.specs = specs

class Specs : 
    def __init__(self , brakes):
        self.brakes = brakes
        

specs = Specs("good brakes")
car = Car("Green" , specs) 

print(json.dumps(car.__dict__))

which returns:

TypeError: Object of type Specs is not JSON serializable

In order to model the "has a" relationship should I create my own to_json function in Car?

Upvotes: 0

Views: 54

Answers (2)

Jon Betts
Jon Betts

Reputation: 3338

A modification of your method to check to see if sub-object have dicts will work in this, and other simple cases. For example:

def to_dict(obj):
    """Convert objects with a '__dict__' method into nested dicts."""

    return {
        key: to_dict(value) if hasattr(value, '__dict__') else value
        for key, value in obj.__dict__.items()
    }


print(json.dumps(to_dict(car)))

There are many cases where this will fall down though:

  • If you use __slots__ in your object
  • If you want to support properties or lists

You could extend the method to try and account for each of these, but it might be time to bite the bullet and either use a serialisation library (as suggested elsewhere), or write your own object specific to_dict() methods as you suggested.

Upvotes: 1

Arun Kaliraja Baskaran
Arun Kaliraja Baskaran

Reputation: 1086

You should consider having an encoder class for your Car class like below

In [8]: import json
   ...:
   ...: class Car :
   ...:     def __init__(self , colour, specs):
   ...:         self.colour = colour
   ...:         self.specs = specs
   ...:
   ...: class Specs :
   ...:     def __init__(self , brakes):
   ...:         self.brakes = brakes
   ...:
   ...:
   ...: specs = Specs("good brakes")
   ...: car = Car("Green" , specs)
   ...:

In [9]:

In [10]: from json import JSONEncoder
    ...:

In [11]: class CarEncoder(JSONEncoder):
    ...:         def default(self, o):
    ...:             op =  o.__dict__
    ...:             op['specs'] =  o.specs.__dict__
    ...:             return op
    ...:
    ...:
    ...:

In [12]: json.dumps(car, cls=CarEncoder)
Out[12]: '{"colour": "Green", "specs": {"brakes": "good brakes"}}'

In [13]:

The CarEncoder's default method will be called by json.dumps. There i have replaced specs with the dictionary of specs..

Upvotes: 1

Related Questions