Python实现JSON序列化和反序列化

在我的应用中,序列化就是把类转成符合JSON格式的字符串,反序列化就是把JSON格式的字符串转换成类。C#的话直接用Newtonsoft.JSON就可以了,非常好用。本来以为python也会有类似的库,但是貌似并没有。网上查了一些python用来实现JSON序列化和反序列化的方法,用的最多的就是json.loads, json.dumps。

# 序列化:将Python对象转换成json字符串
dumps(obj, skipkeys=False, ensure_ascii=True, check_circular=True, allow_nan=True, cls=None, indent=None, separators=None, default=None, sort_keys=False, **kw)

# 反序列化:将json字符串转换成Python对象
loads(s, encoding=None, cls=None, object_hook=None, parse_float=None, parse_int=None, parse_constant=None, object_pairs_hook=None, **kw)
# 序列化:将Python对象转换成json字符串并存储到文件中
dump(obj, fp, skipkeys=False, ensure_ascii=True, check_circular=True, allow_nan=True, cls=None, indent=None, separators=None, default=None, sort_keys=False, **kw)

# 反序列化:读取指定文件中的json字符串并转换成Python对象
load(fp, cls=None, object_hook=None, parse_float=None, parse_int=None, parse_constant=None, object_pairs_hook=None, **kw)

包含自定义类的序列化例子:

from typing import List

class Address:
    def __init__(self,country,city):
        self.country=country
        self.city=city

class Student:
    def __init__(self,name,age,sex,address):
        self.name=name
        self.age=age
        self.sex=sex
        self.address=address

class School:
    def __init__(self,name,address:Address,students:List[Student]):
        self.name=name
        self.address=address
        self.students=students


import json

addr1=Address('China','Beijing')
stu1=Student('lily',20,'female',Address('China','Shanghai'))
stu2=Student('tom',23,'male',Address('China','Shenzhen'))
sch1=School('MathSchool',addr1,[stu1,stu2])

# 先把python对象转换为dict对象,进而才能实现序列化
json_data=json.dumps(sch1,default=lambda o:o.__dict__,indent=2)
print(json_data)

输出结果为:

{
  "name": "MathSchool",
  "address": {
    "country": "China",
    "city": "Beijing"
  },
  "students": [
    {
      "name": "lily",
      "age": 20,
      "sex": "female",
      "address": {
        "country": "China",
        "city": "Shanghai"
      }
    },
    {
      "name": "tom",
      "age": 23,
      "sex": "male",
      "address": {
        "country": "China",
        "city": "Shenzhen"
      }
    }
  ]
}
序列化结果

再把生成的json_data反序列化

sch2=School(**json.loads(json_data))
print(type(sch2))
print(type(sch2.students))
print(type(sch2.address))

输出的结果为:

<class '__main__.School'>
<class 'list'>
<class 'dict'>

可见,这样的反序列化无法直接生成School类中的address属性和students属性。address的类型仍为dict,而students的类型是list。因此需要再把address的dict转换为Address,把students的list转换成List[Student]。

sch2=School(**json.loads(json_data))
sch2.address=Address(**json.loads(json.dumps(sch2.address)))
students=[]
for stu in sch2.students:
    students.append(Student(**json.loads(json.dumps(stu))))
sch2.students=students

也就是说,要这样才能生成一个完整的School类。总感觉太麻烦了,不像用newtonsoft.json那么方便。于是在网上搜到了Refs[2]这篇帖子,用了里面的方法,其实这个方法就是构造了一个创建类的Structure基类。这个方法对于List[Student]这种属性我依然不能很好的解决,总感觉就差了一点点,但是我python用的没有很熟练,也只能这样了。Structure基类的代码为:

class Structure(object):
    _fields=[]
    
    def __init_arg(self,expected_type,value):
        if isinstance(value,expected_type):
            return value
        else:
            return expected_type(**value)
    
    def __init__(self, **kwargs):
        field_names,field_types = zip(*self._fields)
        assert([isinstance(field_name,str) for field_name in field_names])
        assert([isinstance(type_,type)for type_ in field_types])

        for field_name,field_type in self._fields:
            setattr(self,field_name,self.__init_arg(field_type, kwargs.pop(field_name)))
        
        if kwargs:
            raise TypeError('Invalid arguments(s):{}'.format(','.join(kwargs)))

继承该类,那么School, Address, Student类分别为:

class Address(Structure):
    _fields=[('country',str),('city',str)]

class Student(Structure):
    _fields=[('name',str),('age',int),('sex',str),('address',Address)]

class School(Structure):
    _fields=[('name',str),('address',Address),('students',list)]

序列化和反序列化:其中序列化还是使用json,反序列化则没有使用json

import json

addr=Address(**{'country':'China','city':'Beijing'})
stu1=Student(**{'name':'lily','age':18,'sex':'female','address':addr})
stu2=Student(**{'name':'tom','age':19,'sex':'male','address':addr})
sch=School(**{'name':'mathSchool','address':addr,'students':[stu1,stu2]})

# 序列化sch对象,生成字符串
json_data=json.dumps(sch,default=lambda o:o.__dict__,indent=2)

# 将json_data字符串转换为dict
data=json.loads(json_data)

# 反序列化data,得到sch1对象
sch1=School(**data)

# 反序列化可以直接获取到sch1.address对象,但是对于students对象还是不行
print(type(sch1.address))

# list类型的students属性还是要这样生成一下
students=[]
for stu in sch1.students:
    students.append(Student(**stu))

sch1.students=students

 

Refs:

python:序列化与反序列化(json、pickle、shelve) - 秋寻草 - 博客园 (cnblogs.com)

Deserializing nested dictionaries into complex, typed(!) python objects (seanjohnsen.com)

posted @ 2021-09-01 13:59  南风小斯  阅读(603)  评论(0编辑  收藏  举报