python

关注公众号 jb51net

关闭
首页 > 脚本专栏 > python > python json和类对象相互转化

python中实现json数据和类对象相互转化的四种方式

作者:用户70295263214

在日常的软件测试过程中,测试数据的构造是一个占比非常大的活动,对于测试数据的构造,分为结构化的数据构造方式和非结构化的数据构造方式,此篇文章,会通过4种方式来展示json数据与python的类对象相互转化,需要的朋友可以参考下

在日常的软件测试过程中,测试数据的构造是一个占比非常大的活动。对于测试数据的构造,分为结构化的数据构造方式和非结构化的数据构造方式,反映python代码里分别是:

两种方式各有优缺点, 对于业务上有明确业务含义的数据,比如请求数据,建议使用第一种方式,对数据进行建模和定义; 而临时的数据构造,可以使用第二种方式,不需要额外定义一些结构,成本会低一些。在使用于第一种方法时,就会涉及到python中json数据与类对象的相互转化的。

此篇文章,会通过4种方式来展示json数据与python的类对象相互转化

以下的例子,都使用同一个class,并且使用了嵌套的json,算是一个稍微复杂的场景。

class Address():
    def __init__(self, street, number):
        self.street = street
        self.number = number


class User():
    def __init__(self, name, address):
        self.name = name
        self.address = Address()

一、python的原生方法

json to python class的参考:https://stackoverflow.com/a/28352366 

python class to json的参考: https://stackoverflow.com/a/10252138 

以下为代码示例:

import json
from json import JSONEncoder

class Address():
    def __init__(self, street, number):
        self.street = street
        self.number = number

class User():
    def __init__(self, name, address):
        self.name = name
        self.address = Address(**address)  

class MyEncoder(JSONEncoder):
    def default(self, o):
        return o.__dict__

if __name__ == '__main__':
    js = '''{"name":"Cristian", "address":{"street":"Sesame","number":122}}'''
    j = json.loads(js)
    print(j)
    u = User(**j)
    print(json.dumps(u, cls = MyEncoder))
    print(json.dumps(u.__dict__))

执行代码后,输出

{'name': 'Cristian', 'address': {'street': 'Sesame', 'number': 122}}
{"name": "Cristian", "address": {"street": "Sesame", "number": 122}}
Traceback (most recent call last):
***********
    raise TypeError(f'Object of type {o.__class__.__name__} ‘  
TypeError: Object of type Address is not JSON serializable

json转class object时,使用u = User(**j); class object转成json时,对于一级的json,直接使用u.__dict__就可以转成json,而对于嵌套的json,必须使用自定义的JSONEncoder才能转成功。

在以上的转化中,使用了两个python的特性,简单解释一下:

def greet(name, age):
    print(f"Hello, my name is {name} and I am {age} years old.")
person = {'name': 'Alice', 'age': 30}
greet(**person) # 等价于 greet(name='Alice', age=30)

优点:

缺点:

二、jsonpickle库

jsonpickle在github上的介绍如下: 

"Python library for serializing any arbitrary object graph into JSON. It can take almost any Python object and turn the object into JSON. Additionally, it can reconstitute the object back into Python.”

这段话,就说明了这是一个专注于class object -> json的库,而json->object的功能则只支持调用jsonpickle得到的json,再转回class object.

以下为代码示例:

import json
import jsonpickle
class Address():
    def __init__(self, street, number):
        self.street = street
        self.number = number
class User():
    def __init__(self, name, address):
        self.name = name
        self.address = Address(**address)

if __name__ == '__main__':
    js = '''{"name":"Cristian", "address":{"street":"Sesame","number":122}}'''
    j = json.loads(js)
    # jsonpickle中没有一个类似jsonpickle.decode(j, class = User)的方法,所以只能拿第一种方法初始化class
    u = User(**j)
    print(jsonpickle.encode(u, unpicklable=False))
    jp = jsonpickle.encode(u)
    print(jp)
    u2 = jsonpickle.decode(jp)
    print(u2.__class__)

执行后的输出为:

{"name": "Cristian", "address": {"street": "Sesame", "number": 122}}
{"py/object": "__main__.User", "name": "Cristian", "address": {"py/object": "__main__.Address", "street": "Sesame", "number": 122}}
<class '__main__.User'>

一些说明:

1. jsonpickle中没有一个类似jsonpickle.decode(j, class = User)的方法,所以只能拿第一种方法初始化class

3. 转成json时, 调用jsonpickle.encode方法,默认unpicklable=True时,返回值中包含python object的信息,比如对象的类,输出中的"py/object": "_main_.User"就是这些信息

优点:

缺点:

三、cattrs和attrs库

attrs库: https://github.com/python-attrs/attrs。 attrs可以简化类的定义的管理,使用后这些类将自动获得一些有用的特性,如初始化方法(_init_)、__repr__方法、__eq__和__hash__等。实际使用的话,最基础的只需要知道attr.s和attr.ib两个方法即可。

cattrs库: https://github.com/python-attrs/cattrs。cattrs(即“conversion attrs”)是一个与attrs紧密集成的库,它提供了对象到字典(或其他数据结构)的序列化和从字典(或其他数据结构)到对象的反序列化功能。

以下为代码:

import json
import attr
import cattrs

@attr.s
class Address:
    street = attr.ib(type = str)
    number = attr.ib(type = int)

@attr.s
class User:
    name = attr.ib(type = str)
    address = attr.ib(type=Address) # adrress 为Addres类型

if __name__ == "__main__":
    js = '''{"name":"Cristian", "address":{"street":"Sesame","number":122}}'''
    j = json.loads(js)
    u = cattrs.structure(j, User)
    print(u, u.__class__)
    print(cattrs.unstructure(u))

执行后,输出:

User(name='Cristian', address=Address(street='Sesame', number=122), <class '__main__.User'>)
{'name': 'Cristian', 'address': {'street': 'Sesame', 'number': 122}}

一些说明:

优点:

四、pydantic库

Pydantic库: https://github.com/pydantic/pydantic。数据验证和解析的Python库,提供类型注解、数据验证和模型转换功能。使用Pydantic可以定义模型类,验证输入数据并转换为字典或JSON。

直接上代码:

import json
from pydantic import BaseModel, Field

class Address(BaseModel):
    street: str   #pydantic使用类型注解, 来确保使用正确的类型提示来定义字段
    number: int

class User(BaseModel):
    name: str
    address: Address

if __name__ == "__main__":
    js = '''{"name":"Cristian", "address":{"street":"Sesame","number":122}}'''
    j = json.loads(js)
    u = User.parse_obj(j)
    print(u, u.__class__)
    print(u.json())

执行代码后,输出:

name='Cristian' address=Address(street='Sesame', number=122) <class '__main__.User'>
{"name": "Cristian", "address": {"street": "Sesame", "number": 122}}

一些说明: 

优点:

总结:

针对python中类对象和json的相关转化问题, 本文介绍了4种方式,涉及了三个非常强大的python库jsonpickle、attrs和cattrs、pydantic,但是这些库的功能并未涉及太深。在工作中,遇到实际的问题时,可以根据这几种方法,灵活选取。

再回到结构化测试数据的构造,当需要对数据进行建模时,也就是赋予数据业务含义,pydantic应该是首选,目前(2024.7.1)来看,pydantic的生态非常活跃,各种基于pydantic的工具也非常多,建议尝试。

以上就是python中实现json数据和类对象相互转化的四种方式的详细内容,更多关于python json和类对象相互转化的资料请关注脚本之家其它相关文章!

您可能感兴趣的文章:
阅读全文