把JSON数据格式转换为Python的类对象方法详解(两种方法)


Posted in Python onJune 04, 2019

JOSN字符串转换为自定义类实例对象

有时候我们有这种需求就是把一个JSON字符串转换为一个具体的Python类的实例,比如你接收到这样一个JSON字符串如下:

{"Name": "Tom", "Sex": "Male", "BloodType": "A", "Hobbies": ["篮球", "足球"]}

我需要把这个转换为具体的一个Person类的实例,通过对象的方式来进行操作。在Java中有很多实现比如Gson或者FastJosn。如下代码所示(这里不是全部代码,值标识最主要的部分):

import com.alibaba.fastjson.JSONObject;
import com.example.demo.entity.Product;
String a = "{\"gmtCreate\":1559009853000,\"dataFormat\":1,\"deviceCount\":1,\"nodeType\":0,\"productKey\":\"a1U85pSQrAz\",\"productName\":\"温度计\"}";
//JSON字符串反序列化为一个Product对象
Product product = JSONObject.parseObject(a, Product.class);

上述这种需求一般发生在前段传递过来JSON字符串或者其他系统进行RPC通信的时候也发送过来JSON字符串,作为接收端需要反序列化成对象来进行处理,而且Fastjson里还有一个JSONArray.parseArray方法可以转换为对象列表。可是在Python没有像Java中这么方便的东西。

从网上论坛中也看到过一些,不过很多都是效果有但是使用起来麻烦,所以我这里也来说一下我的思路。

方式1:通过josn.loads来实现

#!/usr/bin/env python
# -*- coding: utf-8 -*-
import sys
import json
class Person:
 def __init__(self, data=None):
  self._name = "1"
  self._sex = ""
  self._blood_type = "O"
  self._hobbies = []
  self._date_of_birth = "1900/1/1"
  if data:
   self.__dict__ = data
 # 通过属性的方式来获取和设置实例变量的值,如果不这样那么就只能通过set或者get方法来做
 @property
 def date_of_brith(self):
  return self._date_of_birth
 @date_of_brith.setter
 def date_of_brith(self, date_of_brith):
  self._date_of_birth = date_of_brith
def main():
 try:
  str1 = '{"name": "Tom", "sex": "male", "blood_type": "A", "hobbies": ["篮球", "足球"]}'
  person1 = json.loads(str1, object_hook=Person)
  print(isinstance(person1, Person))
  # 这里你会发现没有date_of_brith这个内容
  print(person1.__dict__)
  # 获取date_of_brith属性值报错,因为JSON字符串不包含这个键,但是类中的实例变量有这个,正常来讲你应该可以获取默认值,但是由于
  # 替换了__dict__,所以就没有了,因为__dict__原本就是实例变量的字典形式,你替换了自然也就找不到原来的了。
  # print(person.date_of_brith)
  # 下面我们通过正常的方式实例化一个对象
  person2 = Person()
  print(person2.__dict__)
  print(person2.date_of_brith)
 except Exception as err:
  print(err)
if __name__ == "__main__":
 try:
  main()
 finally:
  sys.exit()

object_hook的含义是,默认json.loads()返回的是dict,你可以使用object_hook来让其返回其他类型的值,它这里实现的原理就是把你传递进来的JSON字符串传递给了object_hook指定的方法或者类(如果是类的话则会执行__init__方法,其实就是实例化),这时候在类的__init方法中我们通过赋值给self.dict__,其实这就等于对Person类的实例变量做了替换,除非你的JSON字符串的键和实例变量的名称以及数量一致否则你无法通过你在类里定义的实例变量名称获取通过JSON字符串传递进去的值。如下图:

把JSON数据格式转换为Python的类对象方法详解(两种方法)

所以通过上面可以看出来,这个过程不是为实例变量赋值的过程而是一个替换的过程,Python是动态语言这一点和JAVA不同。如果你在程序中用单下划线标识变量为私有(只是规范而不是真正的私有)那么你传递的JSON字符串的键也需要有下划线,这样你通过实例的方法才能获取。既然额外增加下划线不太现实,那么有没有其他办法呢?看方式2

方式2:通过反射机制来实现

先看一下类的定义

#!/usr/bin/env python
# -*- coding: utf-8 -*-
class Person:
 def __init__(self):
  self._name = "1"
  self._sex = ""
  self._blood_type = "O"
  self._hobbies = []
  self._date_of_birth = "1900/1/1"
 def __str__(self):
  """
  输出实例的类名字,而不是一个地址
  :return: 该实例的类名字
  """
  return self.__class__.__name__
 # 当一个方法加上这个装饰器之后,hasattr()中的属性要写成这个方法的名称,而不是实例变量的名称。
 # 如果不加这个装饰器,那么hasattr()中的属性名称要和实例变量的名称保持一致
 @property
 def Name(self):
  return self._name
 @Name.setter
 def Name(self, name):
  self._name = name
 @property
 def Sex(self):
  return self._sex
 @Sex.setter
 def Sex(self, sex):
  self._sex = sex
 @property
 def BloodType(self):
  return self._blood_type
 @BloodType.setter
 def BloodType(self, blood_type):
  self._blood_type = blood_type
 @property
 def Hobbies(self):
  return self._hobbies
 @Hobbies.setter
 def Hobbies(self, hobbies):
  self._hobbies = hobbies
 @property
 def date_of_brith(self):
  return self._date_of_birth
 @date_of_brith.setter
 def date_of_brith(self, date_of_brith):
  self._date_of_birth = date_of_brith

下面看看转换的方法

#!/usr/bin/env python
# -*- coding: utf-8 -*-
import sys
import json
import importlib
def get_instance(str_stream, class_full_path=None):
 """
 :param str_stream: json的字符串形式 '{"Name": "Tom", "Sex": "Male", "BloodType": "A"}'
 :param class_full_path: package.module.class
 :return:
 """
 try:
  json_obj = json.loads(str_stream)
 except Exception as err:
  print("输入的字符串不符合JSON格式,请检查。")
  return None
 if class_full_path is None:
  return json_obj
 else:
  try:
   # 获取模块路径
   module_path = ".".join(class_full_path.split(".")[0:-1])
   # 获取类名称
   class_name = class_full_path.split(".")[-1]
   # 通过模块名加载模块
   CC = importlib.import_module(module_path)
   # 判断是否有class_name所代表的属性
   if hasattr(CC, class_name):
    # 获取模块中属性
    temp_obj = getattr(CC, class_name)
    # 实例化对象
    obj = temp_obj()
    for key in json_obj.keys():
     obj.__setattr__(key, json_obj[key])
    return obj
   else:
    pass
  except Exception as err:
   print(err)
   return None
def main():
 try:
  str1 = '{"Name": "Tom", "Sex": "Male", "BloodType": "A", "Hobbies": ["篮球", "足球"]}'
  person1 = get_instance(str1, class_full_path="AAA.Classes.Person")
  # 查看类型
  print(type(person1))
  # 查看属性
  print(person1.__dict__)
  # 查看指定属性
  print(person1.Name)
 except Exception as err:
  print(err)
if __name__ == "__main__":
 try:
  main()
 finally:
  sys.exit()

__import__() 有2个参数,第一个是类,第二个是fromlist,如果不写fromlist,则按照下面的写法会只导入AAA包,如果fromlist有值则会导入AAA下面的Classes模块cc = __import__("AAA.Classes", fromlist=True)不写fromlist 相当于 import AAA ,如果写了就相当于是from AAA import Classes编程时如果使用动态加载建议使用importlib.import_module(),而不是__import__()。

下面看一下效果:

把JSON数据格式转换为Python的类对象方法详解(两种方法)

可以看到,这样操作之后就是给实例变量赋值而不是像之前那样的替换,而且保留了类中实例变量的私有规范。不过需要说明的是JSON字符串中的键名称要和类里面定义的属性名称一样,也就是键名称要和类中@property装饰的方法同名。我们也可以看到这种使用方式也有默认JSONObject.parseObject的意思。

不过这只是一个简单的实现,只能通过单一JSON字符串生成对象不能生成对象列表。当然有兴趣的朋友可以自己根据这个思路进行扩展。

另外既然无论是loads还是我自己的方法搜需要保证JSON字符串的键和变量名称一致大家就不要纠结于名称一致的问题,但是我的方法做到了保持实例变量命名、操作实例属性时候的规范,同时对类也没有过多的入侵性而不像loads方法中还需要在类的init方法里面增加不必要的内容。在我这个方法中如果能实现忽略大小写通用性就更好了。欢迎大家来提供思路。

总结

以上所述是小编给大家介绍的把JSON数据格式转换为Python的类对象方法详解,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对三水点靠木网站的支持!

Python 相关文章推荐
使用Python设置tmpfs来加速项目的教程
Apr 17 Python
Python中的ConfigParser模块使用详解
May 04 Python
一个基于flask的web应用诞生(1)
Apr 11 Python
Python绘制七段数码管实例代码
Dec 20 Python
利用python实现简单的邮件发送客户端示例
Dec 23 Python
快速了解Python中的装饰器
Jan 11 Python
Sanic框架流式传输操作示例
Jul 18 Python
Python 运行.py文件和交互式运行代码的区别详解
Jul 02 Python
Python hashlib加密模块常用方法解析
Dec 18 Python
Python 如何反方向迭代一个序列
Jul 28 Python
Python3如何实现Win10桌面自动切换
Aug 11 Python
pycharm无法安装cv2模块问题
May 20 Python
Django集成搜索引擎Elasticserach的方法示例
Jun 04 #Python
python添加菜单图文讲解
Jun 04 #Python
Python3.6+Django2.0以上 xadmin站点的配置和使用教程图解
Jun 04 #Python
Python自动化之数据驱动让你的脚本简洁10倍【推荐】
Jun 04 #Python
pandas DataFrame索引行列的实现
Jun 04 #Python
深入浅析Python中的迭代器
Jun 04 #Python
Python学习笔记之读取文件、OS模块、异常处理、with as语法示例
Jun 04 #Python
You might like
PHP+MYSQL开发工具及资源收藏
2007/01/02 PHP
用php将任何格式视频转为flv的代码
2009/09/03 PHP
PHP的mysqli_sqlstate()函数讲解
2019/01/23 PHP
javascript 二进制运算技巧解析
2012/11/27 Javascript
js获取系统的根路径实现介绍
2013/09/08 Javascript
JavaScript初学者建议:不要去管浏览器兼容
2014/02/04 Javascript
jQuery响应鼠标事件并隐藏与显示input默认值
2014/08/24 Javascript
Thinkphp模板没有解析直接原样输出的解决方法
2014/10/31 Javascript
javascript实现俄罗斯方块游戏的思路和方法
2015/04/27 Javascript
JQuery中节点遍历方法实例
2015/05/18 Javascript
jQuery结合CSS制作动态的下拉菜单
2015/10/27 Javascript
轻松实现javascript数据双向绑定
2015/11/11 Javascript
BootStrap 轮播插件(carousel)支持左右手势滑动的方法(三种)
2016/07/07 Javascript
NodeJs的优势和适合开发的程序
2016/08/14 NodeJs
基于JavaScript实现带缩略图的轮播效果
2017/01/12 Javascript
vue2.0 自定义日期时间过滤器
2017/06/07 Javascript
JS 实现分页打印功能
2018/05/16 Javascript
javascript实现支付宝滑块验证码效果
2020/07/24 Javascript
[01:00:53]2018DOTA2亚洲邀请赛3月29日 小组赛B组 iG VS Secret
2018/03/30 DOTA
python中cPickle用法例子分享
2014/01/03 Python
Windows和Linux下使用Python访问SqlServer的方法介绍
2015/03/10 Python
使用python绘制常用的图表
2016/08/27 Python
利用python将pdf输出为txt的实例讲解
2018/04/23 Python
selenium+python实现自动登陆QQ邮箱并发送邮件功能
2019/12/13 Python
《开国大典》教学反思
2014/04/19 职场文书
党委班子剖析材料
2014/08/21 职场文书
转让协议书范本
2014/09/13 职场文书
乡镇机关党员民主评议表自我评价
2014/09/21 职场文书
党的群众路线教育实践活动总结
2014/10/30 职场文书
仓管员岗位职责范本
2015/04/01 职场文书
学雷锋献爱心活动总结
2015/05/11 职场文书
2015小学教师年度工作总结
2015/05/12 职场文书
2015年监理个人工作总结
2015/05/23 职场文书
大学生读书笔记范文
2015/07/01 职场文书
同乡会致辞
2015/07/30 职场文书
Pytest之测试命名规则的使用
2021/04/16 Python