Flask jsonify a list of objects

65,830

Solution 1

Give your EqltByGene an extra method that returns a dictionary:

class EqltByGene(object):
    #

    def serialize(self):
        return {
            'gene_id': self.gene_id, 
            'gene_symbol': self.gene_symbol,
            'p_value': self.p_value,
        }

then use a list comprehension to turn your list of objects into a list of serializable values:

jsonify(eqtls=[e.serialize() for e in my_list_of_eqtls])

The alternative would be to write a hook function for the json.dumps() function, but since your structure is rather simple, the list comprehension and custom method approach is simpler.

You can also be really adventurous and subclass flask.json.JSONEncoder; give it a default() method that turns your EqltByGene() instances into a serializable value:

from flask.json import JSONEncoder

class MyJSONEncoder(JSONEncoder):
    def default(self, obj):
        if isinstance(obj, EqltByGene):
            return {
                'gene_id': obj.gene_id, 
                'gene_symbol': obj.gene_symbol,
                'p_value': obj.p_value,
            }
        return super(MyJSONEncoder, self).default(obj)

and assign this to the app.json_encoder attribute:

app = Flask(__name__)
app.json_encoder = MyJSONEncoder

and just pass in your list directly to jsonify():

return jsonify(my_list_of_eqtls)

You could also look at the Marshmallow project for a more full-fledged and flexible project for serializing and de-serializing objects to Python primitives that easily fit JSON and other such formats; e.g.:

from marshmallow import Schema, fields

class EqltByGeneSchema(Schema):
    gene_id = fields.Integer()
    gene_symbol = fields.String()
    p_value = fields.Float()

and then use

jsonify(eqlts=EqltByGeneSchema().dump(my_list_of_eqtls, many=True)

to produce JSON output. The same schema can be used to validate incoming JSON data and (with the appropriate extra methods), used to produce EqltByGene instances again.

Solution 2

If you look at the docs for the json module, it mentions that you can subclass JSONEncoder to override its default method and add support for types there. That would be the most generic way to handle it if you're going to be serializing multiple different structures that might contain your objects.

If you want to use jsonify, it's probably easier to convert your objects to simple types ahead of time (e.g. by defining your own method on the class, as Martijn suggests).

Share:
65,830
Jared Nedzel
Author by

Jared Nedzel

Updated on June 03, 2020

Comments

  • Jared Nedzel
    Jared Nedzel almost 4 years

    I have a list of objects that I need to jsonify. I've looked at the flask jsonify docs, but I'm just not getting it.

    My class has several inst-vars, each of which is a string: gene_id, gene_symbol, p_value. What do I need to do to make this serializable as JSON?

    My naive code:

    jsonify(eqtls = my_list_of_eqtls)
    

    Results in:

    TypeError: <__main__.EqtlByGene object at 0x1073ff790> is not JSON serializable
    

    Presumably I have to tell jsonify how to serialize an EqtlByGene, but I can't find an example that shows how to serialize an instance of a class.

    I've been trying to follow some of the suggestions show below to create my own JSONEncoder subclass. My code is now:

    class EqtlByGene(Resource):
    
        def __init__(self, gene_id, gene_symbol, p_value):
            self.gene_id = gene_id
            self.gene_symbol = gene_symbol
            self.p_value = p_value
    
    class EqtlJSONEncoder(JSONEncoder):
        def default(self, obj):
            if isinstance(obj, EqtlByGene):
                return {
                       'gene_id'     : obj.gene_id,
                       'gene_symbol' : obj.gene_symbol,
                       'p_value'     : obj.p_value
                }
            return super(EqtlJSONEncoder, self).default(obj)
    
    class EqtlByGeneList(Resource):
        def get(self):
            eqtl1 = EqtlByGene(1, 'EGFR', 0.1)
            eqtl2 = EqtlByGene(2, 'PTEN', 0.2)
            eqtls = [eqtl1, eqtl2]
            return jsonify(eqtls_by_gene = eqtls)
    
    api.add_resource(EqtlByGeneList, '/eqtl/eqtlsbygene')
    app.json_encoder(EqtlJSONEncoder)
    if __name__ == '__main__':
        app.run(debug=True)
    

    When I try to reach it via curl, I get:

    TypeError(repr(o) + " is not JSON serializable")