使用Flask将对象列表转换为JSON格式

62

我有一个对象列表需要转化为JSON格式。我查看了flask的jsonify文档,但是我还是无法理解。

我的类有几个实例变量,每个变量都是字符串:gene_idgene_symbolp_value。我需要做什么才能将其序列化为JSON格式?

我的naive代码:

jsonify(eqtls = my_list_of_eqtls)

结果为:

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

我需要告诉jsonify如何序列化一个,但我找不到一个展示如何序列化类实例的例子。

我一直在尝试按照下面给出的建议创建自己的JSONEncoder子类。现在我的代码是:

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)

当我尝试使用curl访问它时,我收到以下信息:

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

JSON 无法序列化类实例。无论您如何序列化您的类,该序列化都不是规范的 JSON,并且在反序列化 JSON 时需要显式支持。 - lanzz
2个回答

94

给你的EqltByGene类增加一个额外的方法,返回一个字典:

class EqltByGene(object):
    #

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

然后使用列表推导式将您的对象列表转换为可序列化值列表:

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

另一种方法是为json.dumps()函数编写钩子函数,但由于您的结构相当简单,所以列表推导式和自定义方法更简单。

您还可以非常大胆地对flask.json.JSONEncoder进行子类化; 给它一个default()方法,将您的EqltByGene()实例转换为可序列化的值:

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)

将其分配给app.json_encoder属性

app = Flask(__name__)
app.json_encoder = MyJSONEncoder

而且只需直接将您的列表传递给jsonify()

return jsonify(my_list_of_eqtls)

你也可以查看Marshmallow项目,它是一个更完整和灵活的项目,用于将对象序列化和反序列化为Python原语,这些原语很容易适配JSON和其他格式,例如:

from marshmallow import Schema, fields

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

接着使用

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

使用相同的模式可以生成JSON输出。该模式还可用于验证传入的JSON数据,并且(通过适当的额外方法)再次生成EqltByGene实例。


我尝试了: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 JSONEncoder.default(self, obj) .... app.json_encoder(EqtlJSONEncoder)但是我仍然得到: - Jared Nedzel
@user1438352:那么 default 处理程序无法识别您的类类型;由于某种原因,isinstance(obj, EqltByGene) 不为真。在该 if 语句下是否执行了 print 语句? - Martijn Pieters
@user1438352:你在开发服务器中使用动态模块重载吗?如果是这样,很可能出现了某些不同步的情况,你应该关闭并启动一个新的服务器。 - Martijn Pieters
我一直在关闭服务器并重新启动。 - Jared Nedzel
@user1438352:啊,感谢您发布代码;您 没有 正确地注册自定义编码器。使用 app.json_encoder = EqtlJSONEncoder不要 调用预配置的编码器。 - Martijn Pieters
显示剩余6条评论

3

对不起,我是 Python 新手。我看那些文档时感到非常晦涩难懂。你能提供一个例子吗? - Jared Nedzel
@MartijnPieters 我认为 jsonify 会传递 *args, **kwargs,但无论如何,这可能是不使用 jsonify 更好的时候。 - Amber
@Amber:是的,这些参数被传递给dict(),然后那个被传递给json.dumps() - Martijn Pieters

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接