zoukankan      html  css  js  c++  java
  • flask jsonify之序列化时的default函数、jsonify序列化自定义对象

     


    1.看源码

    打开site-package,flask,json,__init__.py

    jsonify回去调用default()函数,我们最关心的就是重写default方法

    我们是不是调用jsonify就一定会调用default呢?答案:不是!

    发现如上图所示,并没有进入jsonify的default方法里,而是直接把字典给序列化出来了。

    那么什么时候会调用default呢?

    结论:如果flask知道如何序列化你传入进来的数据结构的时候,是不会调用default,因为知道如何序列化就直接帮我们序列化了,但是如果我们要序列化一个对象,是我们的user模型,flask默认是不知道怎么去序列化这个模型的,那么就会去调用default函数,为什么会这样的,原因就在于flask不知道怎么序列化,但是它会给我们一个途径,让我们来指明这个数据结构应该怎么序列化,换句话说,default函数最主要的就是我们需要在内部把不能序列化的结构转化为可以序列化的结构,比如我们传入进来的是一个user,user是不能序列化的,但是如果我们可以把user转化成字典,字典是可以序列化的,那么这样就能完成user对象的序列化了,虽然user作为一个模型他不能序列化,但是我们可以把他的信息读取出来,转化为一个字典,从而保证我们整个序列化的成功执行。可以看到default里面的源码,传入的user对象既不是datetime也不是date、uuid.UUID、__html__,所以最后会抛出一个异常

    所以我们要在default中把不能序列化的user转化成可以序列化的格式。

    所以我们继承,然后重写default方法,在重写的函数中实现user的可序列化就OK了

    2、重写默认的default函数,实现自己的序列化机制

    我们不要直接修改源码,要在外部继承JSONEncoder,然后在用自己的方法覆盖原来的default方法。

    1.  
      from flask import Flask, jsonify
    2.  
      class hehe:
    3.  
      name = 'zhangsan'
    4.  
      age = 18
    5.  
       
    6.  
      app = Flask(__name__)
    7.  
      ctx = app.app_context()
    8.  
      ctx.push()
    9.  
      # 上面是解决上下文对象的异常RuntimeError: Working outside of application context.
    10.  
       
    11.  
      a = hehe()
    12.  
      print(a)
    13.  
      jsonify(a) # TypeError: Object of type 'hehe' is not JSON serializable

    可以看到上图代码报错不能序列化a对象,所以我们要在外部继承JSONEncoder,然后在用自己的方法覆盖原来的default方法。

    可以看到即使按照上图所示写,flask还是没有调用我们自己定义的default,所以我们还要在flask里面替换一下

    1.  
      from flask import Flask as _Flask, jsonify
    2.  
      from flask.json import JSONEncoder as _JSONEncoder
    3.  
       
    4.  
       
    5.  
      class JSONEncoder(_JSONEncoder):
    6.  
       
    7.  
      def default(self, o):
    8.  
      pass
    9.  
       
    10.  
       
    11.  
      class Flask(_Flask):
    12.  
      json_encoder = JSONEncoder
    13.  
       
    14.  
       
    15.  
      class hehe():
    16.  
      name = 'zhangsan'
    17.  
      age = 18
    18.  
       
    19.  
       
    20.  
      app = Flask(__name__)
    21.  
      ctx = app.app_context()
    22.  
      ctx.push()
    23.  
      # 上面是解决上下文对象的异常RuntimeError: Working outside of application context.
    24.  
       
    25.  
      a = hehe()
    26.  
      print(a)
    27.  
       
    28.  
      jsonify(a) # 不报错了

    可以看到flask把我们实例化的hehe类当做参数o传递了进来,有两个属性

    3、把对象转化成字典

    3.1 __dict__的方式

    现在我们要把对象转化成字典,因为字典是可以被序列化的, 但是对象不行。我们想到了对象的__dict__内置方法,但是发现没有得到任何的结果,输出的是一个空的json对象。

    这是因为我们在hehe类里面定义的是类的变量而不是实例的变量。类的变量是不会被存放到对象的__dict__当中的。所以加入一个实例变量的时候就有值了(如下图)

    所以我们看到这种方式是可以的,但是我们想把无论类变量还是实例变量都像把它直接序列化,我们就需要用下面的方法把所有的都转成字典

    3.2、定义keys和__getitem__的方式

    python 对象转字典及序列化对象相关问题,__dict__!!!!必看,多坑

    1.  
      class D:
    2.  
      name = 'zhangsan'
    3.  
      age = 18
    4.  
       
    5.  
      def __init__(self):
    6.  
      self.sex = '男'
    7.  
       
    8.  
      def keys(self):
    9.  
      return ('name', 'sex')
    10.  
       
    11.  
      def __getitem__(self, item):
    12.  
      return getattr(self, item)
    13.  
       
    14.  
       
    15.  
      d = D()
    16.  
      print(d.__dict__) # {'sex': '男'}
    17.  
      print(dict(d)) # {'name': 'zhangsan', 'sex': '男'} 如果注销了getitem方法就会报错TypeError: 'D' object is not iterable

    4、最终的代码实现

    1.  
      from flask import Flask as _Flask, jsonify
    2.  
      from flask.json import JSONEncoder as _JSONEncoder
    3.  
       
    4.  
       
    5.  
      class JSONEncoder(_JSONEncoder):
    6.  
      def default(self, o):
    7.  
      if hasattr(o, 'keys') and hasattr(o, '__getitem__'):
    8.  
      print(dict(o))
    9.  
      else:
    10.  
      print("不能序列化对象")
    11.  
       
    12.  
       
    13.  
      class Flask(_Flask):
    14.  
      json_encoder = JSONEncoder
    15.  
       
    16.  
       
    17.  
      class hehe:
    18.  
      name = 'zhangsan'
    19.  
      age = 18
    20.  
       
    21.  
      def __init__(self):
    22.  
      self.sex = '男'
    23.  
       
    24.  
      def keys(self):
    25.  
      return ('name', 'sex')
    26.  
       
    27.  
      def __getitem__(self, item):
    28.  
      return getattr(self, item)
    29.  
       
    30.  
       
    31.  
      app = Flask(__name__)
    32.  
      ctx = app.app_context()
    33.  
      ctx.push()
    34.  
      # 上面是解决上下文对象的异常RuntimeError: Working outside of application context.
    35.  
      a = hehe()
    36.  
      print(a) # <__main__.hehe object at 0x7f0aed3e1e10>
    37.  
      jsonify(a) # {'name': 'zhangsan', 'sex': '男'}

    5、关于default函数的其他知识

    default函数是被递归调用的,之所以我们没看到被递归调用是因为我们定义的类都太简单了,如果对象下面的某个属性是另外一个对象的情况

    只要遇到不能序列化的对象,都会传入default里面让我们来解决,

    例如下面所示default调用了两次:

  • 相关阅读:
    001.Kubernetes简介
    DOCKER学习_018:Docker-Compose文件简介
    DOCKER学习_017:Docker-Compose介绍
    DOCKER学习_016:Docker镜像仓库和HARBOR的简单安装和管理
    DOCKER学习_015:Docker网络补充
    接口漏洞
    Shodan搜索引擎在信息搜集中的应用
    Google在情报搜集中的基础技巧
    数据抓包分析基础
    文件上传之图片木马的学习
  • 原文地址:https://www.cnblogs.com/taosiyu/p/14801910.html
Copyright © 2011-2022 走看看