T1、魔术工具

作者: Brinnatt 分类: 小工具 发布时间: 2024-09-01 11:05

json 封装

class JSONWrapper:
    def __init__(self, data):
        # 这种属性设置会调用__setattr__, 导致实例字典__dict__中不设置__data属性
        # 进而实例属性找不到,会调用__getattr__, 其中又有self.__data又找不到,又会调用__getattr__,无限循环
        # self.__data = data

        # 使用__dict__直接访问实例的属性,避免调用__setattr__
        self.__dict__['_JSONWrapper__data'] = data

    def __getattr__(self, name):
        # 支持属性访问字典键
        if isinstance(self.__data, dict):
            try:
                return self._wrap(self.__data[name])
            except KeyError:
                raise AttributeError(f"Attribute '{name}' not found")
        raise AttributeError(f"'{type(self).__name__}' object has no attribute '{name}'")

    def __setattr__(self, name, value):
        # 如果是字典类型,将新的属性设置到字典中
        if isinstance(self.__data, dict):
            self.__data[name] = value
        else:
            raise TypeError("Cannot set attribute '{}' on non-dict type.".format(name))

    def __getitem__(self, key):
        # 支持字典和列表的索引访问
        if isinstance(self.__data, (dict, list)):
            try:
                return self._wrap(self.__data[key])
            except (KeyError, IndexError) as e:
                raise AttributeError(f"Key or Index '{key}' not found") from e
        raise TypeError(f"Unsupported operation for type '{type(self.__data).__name__}'")

    def __setitem__(self, key, value):
        # 如果是字典类型,设置对应的键值
        if isinstance(self.__data, dict):
            self.__data[key] = value
        # 如果是列表类型,更新索引位置的值
        elif isinstance(self.__data, list):
            self.__data[key] = value
        else:
            raise TypeError("Unsupported operation for type '{}'".format(type(self.__data).__name__))

    def __iter__(self):
        # 迭代内部的列表或字典对象
        if isinstance(self.__data, (list, dict)):
            for item in self.__data:
                yield self._wrap(item)
        else:
            raise TypeError(f"'{type(self.__data).__name__}' object is not iterable")

    def __repr__(self):
        # 返回一个对象的详细表示,适合在开发和调试中使用
        return '{}({})'.format(self.__class__.__name__, repr(self.__data))

    __str__ = __repr__

    @staticmethod
    def _wrap(value):
        # 包装嵌套的字典和列表
        if isinstance(value, (dict, list)):
            return JSONWrapper(value)
        return value

    def __bool__(self):
        # 判断封装的对象是否为空
        if not self.__data:
            return False
        return True

    # 兼容 Python 2.7
    __nonzero__ = __bool__

    @property
    def raw(self):
        # 拿数据结构,非对象
        return self.__data

# 示例用法
data = {
    "status": "normal",
    "storage_pool": [
        {
            "logical_pools": [
                "default_ec_data",
                "default_ec_data_replication"
            ],
            "name": "storage_pool_1",
            "id": 1
        }
    ],
    "extern": {
        "pool_partition": {
            "eds_fe997806": {
                "phxtier": [
                    {
                        "data": {
                            "size": 79,
                            "sn": "eds-fe997806-0",
                            "device_no": "2"
                        }
                    },
                    {
                        "data": {
                            "size": 80,
                            "sn": "eds-fe997806-1",
                            "device_no": "3"
                        }
                    }
                ]
            }
        }
    }
}

wrapped_data = JSONWrapper(data)

# 示例访问
print(wrapped_data.status)  # 输出: normal
print(wrapped_data.storage_pool[0].name)  # 输出: storage_pool_1
print(wrapped_data.extern.pool_partition["eds_fe997806"].phxtier[0].data.size)  # 输出: 79

wsgi 中的 json.dump 为什么不能序列化类对象,有 circle reference 问题?

在使用 wsgi 中的 json.dumps 或类似的 JSON 序列化方法时,无法直接序列化自定义类的对象,这通常是因为 Python 的默认 JSONEncoder 不知道如何处理复杂的对象。

json.dumps 默认只能处理以下数据类型:

  • 基本类型:如 str, int, float, bool, None
  • Python 内置的容器类型:如 list, tuple, dict
  • 自定义的类对象(如 JSONWrapper、数据库模型对象等)不属于这些类型,因此 json.dumps 无法直接序列化这些对象。尝试序列化时会引发 TypeError: Object of type X is not JSON serializable 异常。
  • 而 circle reference(循环引用)的问题通常出现在自定义类中,如果对象的属性或嵌套结构导致相互引用或对象的递归结构,JSON 序列化会认为它是一个循环引用,无法处理。

如何处理无法序列化的问题?

要让 json.dumps 正确处理自定义类对象,需要提供一个自定义的序列化器或将对象转换为 Python 内置的数据结构(如 dict)。

标签云