3

How could I return a proper dict that has an array of Objects on it? I'm making a flask API and I'm not getting a good result that I can consume later as a json object.

Here's the output and how I'm making it Output:

{
    "starttime": "2018-10-19 12:10:00",
    "endtime": "2018-10-19 12:11:00",
    "env": "TEST",
    "statistics": "{\"TEST\": {\"queryUsers\": {\"calls\": 43, \"avgtime\": 1, \"errors\": 0, \"first_error\": null, \"last_error\": null, \"timeouts\": 0, \"first_to\": null, \"last_to\": null},
...

Router:

def get(self, env):
if env == 'TEST':
    start = "2018-10-19 12:10:00"
    end = "2018-10-19 12:11:00"
    stats = self.scontroller.getStatistics(start, end, 'ALL')

    return {'starttime': start, 'endtime': end, 'env': env, 'statistics': json.dumps(stats, cls=MyJSONEncoder)}, 200

return "ENV not found", 404

And if i try to return this instead

return {'starttime': start, 'endtime': end, 'env': env, 'statistics': stats}, 200

I'll get:

    raise TypeError(f'Object of type {o.__class__.__name__} '
    TypeError: Object of type Statistic is not JSON serializable

My json encoder:

class MyJSONEncoder(JSONEncoder):
    def default(self, obj):
        if isinstance(obj, Metric):
            pass
        if isinstance(obj, Statistic):
            return vars(obj)

        return JSONEncoder.default(self, obj)

The dict that should go in the statistics follows the model below:

{
  "ENVNAME":{
            "APINAME":{
                           <Object Statistic>,
                           <Object Statistic>,
                           <Object Statistic>,
                           <Object Statistic>
                       },
            "ANOTHERAPI": {...}
           }
}

And it's mounted that way:

Statistic.py

class Statistic():

def __init__(self, calls, avgtime, errors, first_error, last_error, timeouts, first_to, last_to):
    self.calls = calls
    self.avgtime = avgtime
    self.errors = errors
    self.first_error = first_error
    self.last_error = last_error
    self.timeouts = timeouts
    self.first_to = first_to
    self.last_to = last_to

On the controller:

stats[env][api] = Statistic(calls, avgtime, errors, first_er, last_er, tos, first_to, last_to)

Any help is appreciated.

Thank you!

davis
  • 1,216
  • 5
  • 14
  • 27
  • 1
    please dont do this ... the future you will thank you ... or your co workers – Joran Beasley Oct 23 '18 at 02:05
  • @JoranBeasley what do you recommend? – davis Oct 23 '18 at 14:19
  • add a to_dict() to your class that will return a dict representation of your class ... and maybe add a from dict to allow you to reconstruct it from that dict ... explicit is always preferred over implicit ... – Joran Beasley Oct 23 '18 at 17:50
  • this is going to be an api only to get data and jsonify worked pretty well, as seen in the response below. should I still do this for each and every class? – davis Oct 24 '18 at 21:44
  • the problem is that encoders hide functionality and are non-standard ... when something doesnt work it can get really hard to track down – Joran Beasley Oct 24 '18 at 21:48

1 Answers1

3

Fixed by using flask.jsonify() and declaring app.json_encoder = MyJSONEncoder in the app starter.

        response = jsonify(starttime=start, endtime=end, env=env, statistics=stats)
        response.status_code = 200
        return response

Hope it helps others too.

davis
  • 1,216
  • 5
  • 14
  • 27
  • 2
    Hi, I would recomend using flask `jsonify()` instead of `json.dums()`, but for further details, you can read this [answer](https://stackoverflow.com/a/13172658/8843270), it has quite good explanation – simkusr Oct 23 '18 at 05:05
  • 1
    Hi @RolandasŠimkus, This worked pretty well. Thank you – davis Oct 23 '18 at 14:59