找回密码
 立即注册

QQ登录

只需一步,快速开始

扫一扫,访问微社区

查看: 309|回复: 0

[代码与实例] 十分好用的Python实体类与Json相互转换框架

1

主题

2

帖子

2

积分

贫民

积分
2
大卫 发表于 2018-2-7 11:18:27 | 显示全部楼层 |阅读模式
(代码见附件  或  https://github.com/hdw09/jsonmodel
1.Python 需要json2model吗?
Python 需要json2model吗?最近用Python写crash监控预警系统,系统在最开始时,加载请求一个监控需要的配置接口。接口返回一个Json结构的数据,如下:
  1. {
  2.     "projectName": "X项目",
  3.     "projectID": "xproject",
  4.     "administrator": "huangdawei@163.com",
  5.     "threshold":30,
  6.     "type":"iOS",
  7.     "needCreateTask":false,
  8.     "flowTaskName":"FLOWPEO",
  9.     "channels": [
  10.       {
  11.         "channelName": "平台业务",
  12.         "channelID": " PlatPlugin",
  13.         "manager": "huangdawei@163.com",
  14.         "threshold":5
  15.       },
  16.       {
  17.         "channelName": "用户评论回复",
  18.         "channelID": " disPlugin",
  19.         "manager": "huangdawei@163.com",
  20.         "threshold":5
  21.       }
  22.     ],
  23.     "members": [
  24.       {
  25.         "misID": "huagndawei@meituan.com",
  26.         "channelID": "FoodPlugin",
  27.         "type": "rd"
  28.       },
  29.       {
  30.         "misID": "huangdawei@163.com",
  31.         "channelID": "NAN",
  32.         "type": "qa"
  33.       },
  34.       {
  35.         "misID": "huangdawei@163.com",
  36.         "channelID": "NAN",
  37.         "type": "leader"
  38.       }
  39.     ]
  40.   }
复制代码
在使用Java和swift开发时,从接口拿到数据后,紧接着就把它转换为实体类。通过实体类的属性去访问各个信息字段。这样做不仅对IDE友好,输入一个`.`自动提示有哪些属性。而且可以避免由于拼写造成的crash ,如`project['members']`写作`project['mmembers']`。但是Python里默认没有相应的转换功能,我们在写代码时一般如下面这样:
  1. if respond.status_code == 200:
  2.     self.projectConfig = respond.json()
  3. # self.projectConfig 是一个字典(key_value)
复制代码
于是就有一大堆的 `if x in X`的判断代码,比如:
  1. if 'channels' in self.projectConfig and len(self.projectConfig['channels']) > 0:
  2.     for channel in self.projectConfig['channels']
复制代码
不使用实体类的另外一个缺点就是不能抽象的描述数据,不能将相关的方法与数据结合。比如我们用`for channel in self.projectConfig['channels']`取出来的channel不是一个对象,这在写代码的时候会十分难受,有一大推的本应该封装在"Channel类“中的操作被放在高层,比如:
  1. def findFirstLevelUsers(self):
  2.     if 'members' in self.mainProject and len(self.mainProject['members']) > 0:
  3.         return map(lambda member: member['misID'], filter(lambda member: member['type'] == 'rd' or member['type'] == 'qa', self.mainProject['members']))
  4.     else:
  5.         return []
复制代码
这个函数本应该封装在project中,现在要放在预警处理类中,造成预警处理类逻辑很多。程序整体的封装和内聚都很差。所以把json对象对应的字典变成实体类,应该是一个常规需求,在Python中应该也有现成的解决办法。但是由于接触Python时间不长,没有在网上找到十分令人满意的转换方法。

2.现有的json2model方法或框架

搜索到最多的方案是:

  1. class MyClass:  
  2.     #初始化  
  3.     def __init__(self):  
  4.     self.a=2  
  5.     self.b='bb'
  6.         
  7. myClass2 = MyClass()
  8. myClass2.__dict__ = json.loads(myClassJson)
复制代码
这种方法有一个很严重的缺陷,就是实体类无法嵌套定义,比如`MyClass`中包含`OtherClass`类的属性就不行了。 可以使用下面方法解决这个问题:
  1. load = json.loads(dump,object_hook = dict2object)
复制代码
但是要每个类都要写一个`object_hook`函数,对开发人员带来的负担很大。这个问题如此明显,网上应该有相应的框架做了这个事情才对,但不知道是不是我搜索关键词的问题,在github上搜索`json model`,真的没一个好用的,于是索性自己写一个。(PS:我没有专门深入找过相应的框架,觉得很简单就自己写了,如果有现有的好用的库还请告诉我,多谢)

3.Python Json to Model实现

这种框架在Android和iOS上十分泛滥,各种优缺点的框架都有。总的来说满足`代码侵入性低``易用性强``稳定性高`这三点的框架都是好框架。

  • 代码代码侵入性低是指,不要让用户非得继承框架中的某个BaseModel,不要让用户添加或重写某种转换方法
  • 易用性是指让用户非常方便的描述一个对象成员所属的类是哪个,对象数组成员中数组元素所属的类是哪个。(这两点,是所有框架都必须要的,因为无论框架做的再好都要让用户告诉程序这两个信息)
  • 稳定性是指,一个来自文件或网络的json格式是不确定的,框架要保证无论json里面是啥,程序都能正常运行,不崩溃,合理的告诉用户哪里有问题


我写了一个叫JsonModel的小框架,用法大概就如下这样。大家看看是不是满足上面三个特性

  1. import json
  2. from jsonModel import jsonModel

  3. @jsonModel()
  4. class Pet(object):

  5.     def __init__(self):
  6.         self.name = ""

  7. @jsonModel()
  8. class Car(object):

  9.     def __init__(self):
  10.         self.registration_number = ""
复制代码
用户通过修饰符对实体类进行修饰就行了,对用户代码的侵入性很低。如果用户想要更换解析框架只要把修饰去除就好了。通过可选的objectMap={}, listClassMap={}参数很简单就可以对映射做出描述。

最后大家看代码吧,总共不超过60行就搞定了,如果大家喜欢给个星星吧~

github链接(https://github.com/hdw09/jsonmodel)。

在使用的过程中有发现需要添加两个针对数组的方法,补充进来,更新的示例如下:

  1. jsonListString = """[{
  2.     "car": {
  3.         "color": "red",
  4.         "registration_number": "ASDF777",
  5.         "engine_capacity": 5.0
  6.     },
  7.     "surname": "Bravo",
  8.     "name": "Johny",
  9.     "nickname": "hello",
  10.     "pets": [
  11.         {
  12.             "name": "Garfield"
  13.         },
  14.         {
  15.             "age": 9,
  16.             "name": "Dogmeat"
  17.         }
  18.     ]
  19. },{
  20.     "car": {
  21.         "color": "red",
  22.         "registration_number": "ASDF777",
  23.         "engine_capacity": 5.0
  24.     },
  25.     "surname": "Bravo",
  26.     "name": "Johny",
  27.     "nickname": "hello",
  28.     "pets": [
  29.         {
  30.             "name": "Garfield"
  31.         },
  32.         {
  33.             "age": 9,
  34.             "name": "Dogmeat"
  35.         }
  36.     ]
  37. },{
  38.     "car": {
  39.         "color": "red",
  40.         "registration_number": "ASDF777",
  41.         "engine_capacity": 5.0
  42.     },
  43.     "surname": "Bravo",
  44.     "name": "Johny",
  45.     "nickname": "hello",
  46.     "pets": [
  47.         {
  48.             "name": "Garfield"
  49.         },
  50.         {
  51.             "age": 9,
  52.             "name": "Dogmeat"
  53.         }
  54.     ]
  55. }]"""


  56. """
  57. 通过调用描述符动态生成Person的类方法objectArrayFromJsonArray()
  58. 可以吧json list 直接转换为object list
  59. """
  60. personList = Person.objectArrayFromJsonArray(json.loads(jsonListString))
  61. print(personList)
  62. print(json.dumps(personList[0].toKeyValue()))

  63. """
  64. 通过调用描述符动态生成Person的类方法objectArrayToJsonArray()
  65. 可以反过来吧object list 转换为 json keyvalue list
  66. """
  67. print(json.dumps(Person.objectArrayToJsonArray(personList)))
复制代码





归档.zip

1.9 KB, 下载次数: 6

代码

回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

快速回复 返回顶部 返回列表