把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使用tensorflow入门指南
Feb 09 Python
python基础教程项目五之虚拟茶话会
Apr 02 Python
神经网络(BP)算法Python实现及应用
Apr 16 Python
多个应用共存的Django配置方法
May 30 Python
python高阶爬虫实战分析
Jul 29 Python
Python操作配置文件ini的三种方法讲解
Feb 22 Python
50行Python代码获取高考志愿信息的实现方法
Jul 23 Python
Pycharm小白级简单使用教程
Jan 08 Python
python读取多层嵌套文件夹中的文件实例
Feb 27 Python
Python使用pyyaml模块处理yaml数据
Apr 14 Python
python -v 报错问题的解决方法
Sep 15 Python
如何通过python检查文件是否被占用
Dec 18 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 采集程序原理分析篇
2010/03/05 PHP
php 获取本机外网/公网IP的代码
2010/05/09 PHP
怎样使用php与jquery设置和读取cookies
2013/08/08 PHP
php检测数组长度函数sizeof与count用法
2014/11/17 PHP
利用php抓取蜘蛛爬虫痕迹的示例代码
2016/09/30 PHP
php无限极分类实现方法分析
2019/07/04 PHP
[推荐]javascript 面向对象技术基础教程
2009/03/03 Javascript
一些常用且实用的原生JavaScript函数
2010/09/08 Javascript
SwfUpload在IE10上不出现上传按钮的解决方法
2013/06/25 Javascript
IE8的JavaScript点击事件(onclick)不兼容的解决方法
2013/11/22 Javascript
javascript删除字符串最后一个字符
2014/01/14 Javascript
兼容主流浏览器的JS复制内容到剪贴板
2014/12/12 Javascript
js操作XML文件的实现方法兼容IE与FireFox
2016/06/25 Javascript
AngularJS表达式讲解及示例代码
2016/08/16 Javascript
JS获取当前页面名称的简单实例
2016/08/19 Javascript
JavaScript用JSONP跨域请求数据实例详解
2017/01/06 Javascript
使用jQuery实现一个类似GridView的编辑,更新,取消和删除的功能
2017/03/15 Javascript
从setTimeout看js函数执行过程
2017/12/19 Javascript
微信小程序实现联动选择器
2019/02/15 Javascript
微信小程序云开发之数据库操作
2019/05/18 Javascript
vue中的mescroll搜索运用及各种填坑处理
2019/10/30 Javascript
解决vue项目运行npm run serve报错的问题
2020/10/26 Javascript
[01:11:27]2018DOTA2亚洲邀请赛小组赛 A组加赛 Newbee vs Optic
2018/04/03 DOTA
python生成随机密码或随机字符串的方法
2015/07/03 Python
python2与python3共存问题的解决方法
2018/09/18 Python
Python从文件中读取数据的方法讲解
2019/02/14 Python
使用python PIL库实现简单验证码的去噪方法步骤
2019/05/10 Python
如何通过python实现IOU计算代码实例
2020/11/02 Python
python 怎样进行内存管理
2020/11/10 Python
Html5如何唤起百度地图App的方法
2019/01/27 HTML / CSS
2015年体育教学工作总结
2015/05/20 职场文书
楚门的世界观后感
2015/06/03 职场文书
Oracle更换为MySQL遇到的问题及解决
2021/05/21 Oracle
opencv-python图像配准(匹配和叠加)的实现
2021/06/23 Python
Windows server 2012 配置Telnet以及用法详解
2022/04/28 Servers
一文搞懂PHP中的抽象类和接口
2022/05/25 PHP