Python的json序列化

引言

在开发中,经常使用json格式的数据来相互传递,json序列化和反序列化也是常规操作,在java中有各种第三方json库来实现,比如Gson,jackson,fastjson,后者是我经常在用的。而在Pyhon中如何实现呢,Python自带了json库,json序列化和反序列化主要有两个方法:

  • dumps() : 序列化,将对象转换为json字符串
import json

person = {"name": "tom", "age": 20}

result = json.dumps(person)

print(result) # {"name": "tom", "age": 20}
print(type(result)) # <class 'str'>

值得一提的是dumps()方法还支持接收一个indent参数,参数类型为int类型,当传递这个参数将让数据变得更加美观易读

result = json.dumps(person,indent=4)

print(result)
"""
{
    "name": "tom",
    "age": 20
}
"""
  • loads() :反序列化,将json字符串转换为字典
p = json.loads(result)
print(p) # {'name': 'tom', 'age': 20}
print(type(p)) # <class 'dict'>

序列化自定义类

class Person:
    def __init__(self, name, age) -> None:
        self.name = name
        self.age = age

p = Person("tom", 20)
result = json.dumps(p) # TypeError: Object of type Person is not JSON serializable

如上面代码所示,在尝试对一个自定义类的实例json序列化时,会报TypeError错误,那为什么会这样呢

序列化支持

在Python中,一个对象是否可以被序列化取决于它的类型和内容。
以下是一些常见的对象和类型,以及它们是否可以被序列化

  • 基本数据类型
    int, float, str, bool, None,这些都是可以直接序列化的
  • 序列化支持的对象
    dict,list,tuple,set,这些对象也是可以直接序列化的
  • 不可序列化的对象
    • 方法:任何类型的方法都不能被序列化。
    • 类:类本身不能被序列化,但是类的实例可以被序列化。
    • 文件描述符:如打开的文件或网络连接等,这些对象通常是不可序列化的。
    • 循环引用:如果对象之间存在循环引用,则这些对象也不能被序列化。
  • 模块和函数
    模块和函数通常是不可序列化的,因为它们是Python运行时的抽象,不是可存储的
  • 复杂的对象
    例如,自定义类、自定义的集合类、装饰器等,这些对象通常需要实现序列化支持才能被序列化

自定义类序列化

在面向对象中,我们通常将数据结构类型定义成一个类,而在python中,自定义类本身是不支持序列化的,需要实现序列化支持,才能被序列化。
如何实现:
字典对象是支持序列化的,而我们自定义的类会有一个__dict__的属性(从object继承)这个属性是一个包含实例对象属性值的字典对象,可以通过该属性获取字典对象并传给json.dumps()

class Person:
    def __init__(self, name, age):
        self.name = name
        self.age = age


p = Person("tom", 20)

print(p.__dict__) # {'name': 'tom', 'age': 20}
print(type(p.__dict__)) # <class 'dict'>

result = json.dumps(p.__dict__)

print(result) # {"name": "tom", "age": 20}
print(type(result)) # <class 'str'>

目前仅限于简单的自定义类,如果涉及到类的某一个属性的数据类型指向了其他自定义类,那么json.dumps(obj.dict)方式将不再适用,会报TypeError错误

class Feature:
    def __init__(self, height, weight) -> None:
        self.height = height
        self.weight = weight


class Person:
    def __init__(self, name, age, feature):
        self.name = name
        self.age = age
        self.feature = feature


feature = Feature(175, 125)

p = Person("tom", 20, feature)

result = json.dumps(p.__dict__) # TypeError: Object of type Feature is not JSON serializable

自定义JSONEncoder

分析

从下面dupms方法源码可以看出,dumps方法序列化核心主要是JSONEncoder对象,我们可以通过继承JSONEncoder并重写encode方法实现自定义JSONEncoder,并将自定义的encoder传给dumps方法,来达到序列化的目的

# dumps方法源码
def 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):
    if (not skipkeys and ensure_ascii and
        check_circular and allow_nan and
        cls is None and indent is None and separators is None and
        default is None and not sort_keys and not kw):
        return _default_encoder.encode(obj)
    if cls is None:
        cls = JSONEncoder
    return cls(
        skipkeys=skipkeys, ensure_ascii=ensure_ascii,
        check_circular=check_circular, allow_nan=allow_nan, indent=indent,
        separators=separators, default=default, sort_keys=sort_keys,
        **kw).encode(obj)

实现思路

利用Python对象的特性将实例对象转换为字典对象,再调用父类方法序列化字典对象

from typing import override
import json

class CustomJSONEncoder(json.encoder.JSONEncoder):
    @override
	def encode(self, obj) -> str:
        dct = self.convert(obj)
        return super().encode(dct)

    def convert(self, obj):
        if hasattr(obj, "__dict__"):
            dct = obj.__dict__
            for k, v in dct.items():
                f = self.convert(v)
                dct[k] = f
            return dct
        if isinstance(obj, (list, set, tuple)):
            seq = []
            for e in obj:
                seq.append(self.convert(e))
            return seq
        return obj




class Feature:
    def __init__(self, height, weight) -> None:
        self.height = height
        self.weight = weight


class Person:
    def __init__(
        self,
        name,
        age,
        feature=None,
        favorites=[],
        children=[],
    ):
        self.name = name
        self.age = age
        self.feature = feature
        self.favorites = favorites
        self.children = children


james = Person("James", 10)
jimmy = Person("Jimmy", 7)

favoites = ("打篮球", "鸡胸肉")
feature = Feature(175, 125)
peson = Person("tom", 32, feature, favoites, children=[james, jimmy])

result = json.dumps(peson, cls=CustomJSONEncoder, ensure_ascii=False)

print(result)
"""
{
    "name": "tom",
    "age": 32,
    "feature": {
        "height": 175,
        "weight": 125
    },
    "favorites": [
        "打篮球",
        "鸡胸肉"
    ],
    "children": [
        {
            "name": "James",
            "age": 10,
            "feature": null,
            "favorites": [],
            "children": []
        },
        {
            "name": "Jimmy",
            "age": 7,
            "feature": null,
            "favorites": [],
            "children": []
        }
    ]
}
"""
print(type(result)) # <class 'str'>

posted on 2024-06-09 22:46  luyifo  阅读(77)  评论(0)    收藏  举报

导航