把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 性能优化技巧总结
Nov 01 Python
Python安装Numpy和matplotlib的方法(推荐)
Nov 02 Python
在cmd命令行里进入和退出Python程序的方法
May 12 Python
Python多重继承的方法解析执行顺序实例分析
May 26 Python
示例详解Python3 or Python2 两者之间的差异
Aug 23 Python
在Pycharm中自动添加时间日期作者等信息的方法
Jan 16 Python
python把ipynb文件转换成pdf文件过程详解
Jul 09 Python
django中SMTP发送邮件配置详解
Jul 19 Python
基于Python测试程序是否有错误
May 16 Python
新版Pycharm中Matplotlib不会弹出独立的显示窗口的问题
Jun 02 Python
如何使用 Python 读取文件和照片的创建日期
Sep 05 Python
Python 居然可以在 Excel 中画画你知道吗
Feb 15 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
一台收音机,让一家人都笑逐颜开!
2020/08/21 无线电
PHP学习笔记 (1) 环境配置与代码调试
2011/06/19 PHP
php判断电脑访问、手机访问的例子
2014/05/10 PHP
ThinkPHP做文字水印时提示call an undefined function exif_imagetype()解决方法
2014/10/30 PHP
ThinkPHP上使用多说评论插件的方法
2014/10/31 PHP
基于PHP实现的多元线性回归模拟曲线算法
2018/01/30 PHP
[原创]保存的js无法执行的解决办法
2007/02/25 Javascript
JavaScript 异步调用框架 (Part 1 - 问题 & 场景)
2009/08/03 Javascript
javascript小组件 原生table排序表格脚本(兼容ie firefox opera chrome)
2012/07/25 Javascript
Javascript异步编程的4种方法让你写出更出色的程序
2013/01/17 Javascript
jQuery对象初始化的传参方式
2015/02/26 Javascript
jQuery-1.9.1源码分析系列(十一)DOM操作续之克隆节点
2015/12/01 Javascript
JS判断iframe是否加载完成的方法
2016/08/03 Javascript
JavaScript使用delete删除数组元素用法示例【数组长度不变】
2017/01/17 Javascript
react.js 翻页插件实例代码
2017/01/19 Javascript
javascript设计模式之中介者模式学习笔记
2017/02/15 Javascript
BootStrap模态框和select2合用时input无法获取焦点的解决方法
2017/09/01 Javascript
JavaScript实现JSON合并操作示例【递归深度合并】
2018/09/07 Javascript
Vux+Axios拦截器增加loading的问题及实现方法
2018/11/08 Javascript
vue选项卡切换登录方式小案例
2019/09/27 Javascript
全网小程序接口请求封装实例代码
2020/11/06 Javascript
Python学习小技巧之列表项的拼接
2017/05/20 Python
python逐行读写txt文件的实例讲解
2018/04/03 Python
Python使用pyautocad+openpyxl处理cad文件示例
2019/07/11 Python
基于python调用jenkins-cli实现快速发布
2020/08/14 Python
idealfit英国:世界领先的女性健身用品和运动衣物品牌
2017/11/25 全球购物
DeinDesign德国:设计自己的手机壳
2019/12/14 全球购物
在校生钳工实习自我鉴定
2013/09/19 职场文书
打架检讨书50字
2014/01/11 职场文书
物业总经理岗位职责
2014/02/28 职场文书
教师求职信
2014/06/17 职场文书
护理学院专科毕业生求职信
2014/06/28 职场文书
护理心得体会范文
2016/01/22 职场文书
vue实现列表拖拽排序的示例代码
2022/04/08 Vue.js
golang操作redis的客户端包有多个比如redigo、go-redis
2022/04/14 Golang
MySQL 原理与优化之原数据锁的应用
2022/08/14 MySQL