Python-Json异常:Object of type Decimal is not JSON serializable

文章讲述了在Python中处理Decimal和bytes类型数据时遇到的JSON序列化问题,介绍了如何通过CustomJsonEncoder类将Decimal转换为float,以及处理bytes编码以确保中文输出正确。作者还分享了一个toJson函数,便于在代码中统一处理这些转换。

源起:

使用python分离出一串文本,因为是看起来像整数,结果json转换时发生异常:TypeError: Object of type Decimal is not JSON serializable

msgInfo={"uid":3232324232}
json.dumps(msgInfo, ensure_ascii=False)

原因:

decimal格式不能被json.dumps正确处理。json.dumps函数发现字典里面有 Decimal类型的数据,无法JSON serializable

同样的问题也会出现在转换bytes数据时。

解决办法:

解决方法:是检查到Decimal类型的值转化成float类型

对于bytes则需要做一层编码。

正好为了防止中文出错,每次解析加ensure_ascii挺麻烦的。如果不加ensure_ascii,很多时候中文会被转译为:"\u4e2d\u56fd"这样的格式。

原因在于python序列化时对中文默认使用的ascii编码.想输出真正的中文需要指定ensure_ascii=False。

顺手封装为一个公共函数。方便使用。

顺手把时间 转换和bytes处理也一并加上。

后面直接使用toJson(data)就可以。


def toJson(data, indent=None):
    """
    数据转换为Json。
    :param data:
    :param indent:
    :return:
    """
    return json.dumps(data, cls=CustomJsonEncoder, ensure_ascii=False, indent=indent)


class CustomJsonEncoder(json.JSONEncoder):
    """
    Json解析器,解决识别Decimal出错的问题
    """

    def default(self, obj):
        if isinstance(obj, datetime.datetime):
            return obj.strftime("%Y-%m-%d %H:%M:%S")
        if isinstance(obj, bytes):
            return str(obj, encoding='utf-8')
        if isinstance(obj, int):
            return int(obj)
        elif isinstance(obj, float):
            return float(obj)
        elif isinstance(obj, decimal.Decimal):
            return float(obj)
        # elif isinstance(obj, array):
        #    return obj.tolist()
        else:
            return super(CustomJsonEncoder, self).default(obj)

同open读文件一样,python对很多问题貌似并不太符合我们的中文习惯。每次都需要加上encoding='utf-8'不然常常会读中文内容时出现问题。

本文由博客一文多发平台 OpenWrite 发布!

<think> 我们面对的错误是:`TypeError: Object of type AsyncQueryWrapper is not JSON serializable` 这个错误发生在尝试将包含 `AsyncQueryWrapper` 类型对象的对象序列化为 JSON 时。 引用[1]到[3]中提到的错误类似,都是因为JSON序列化器无法处理特定类型(如Undefined、Decimal、bytes)而引发的。 解决思路: 1. **理解问题**:JSON序列化仅支持基本类型(如dict, list, str, int, float, bool, None)。任何自定义类型或特殊类型都需要被转换成这些基本类型。 2. **定位来源**:`AsyncQueryWrapper` 可能是某个异步查询的结果包装器(例如在Django或SQLAlchemy等ORM中),它可能是一个数据库查询对象,还没有被求值。 3. **解决方案**: a. **评估查询**:如果 `AsyncQueryWrapper` 是一个惰性查询对象,我们需要执行查询以获取实际数据。 b. **自定义序列化**:创建一个自定义的JSON编码器,将 `AsyncQueryWrapper` 转换为可序列化的类型(如列表或字典)。 c. **避免传递非序列化对象**:在将数据传递给序列化器之前,确保数据已经是基本类型。 具体步骤: 方法一:在序列化之前将 `AsyncQueryWrapper` 转换为可序列化的数据结构 - 如果是异步ORM(例如Django的异步查询),你可能需要等待查询结果,然后将其转换为字典或列表。 - 示例代码: ```python # 假设你有一个包含 AsyncQueryWrapper 的对象 # 在序列化之前,先解析这个对象 from django.core import serializers async def my_view(request): # 假设 queryset 是一个异步查询集(AsyncQuerySet) queryset = MyModel.objects.all() # 异步执行查询并获取结果列表 results = await queryset # 将结果序列化为JSON字符串 data = serializers.serialize('json', results) return JsonResponse(data, safe=False) ``` 或者,如果你使用的是TortoiseORM(异步ORM): ```python from tortoise.contrib.sanic import register_tortoise from tortoise import Tortoise async def list_objects(request): objects = await MyModel.all() # 将每个对象转换为字典 data = [obj.to_dict() for obj in objects] return json(data) ``` 方法二:自定义JSON编码器 - 创建一个继承自 `json.JSONEncoder` 的类,并重写 `default` 方法。 - 在 `default` 方法中,检查对象是否为 `AsyncQueryWrapper` 类型,然后处理它(例如执行查询并返回结果)。 - 注意:在编码器中执行异步操作可能是困难的,因为 `json.dumps` 是同步的。因此,我们更推荐在序列化之前就处理好数据。 因此,更实际的做法是避免在需要序列化的数据结构中包含 `AsyncQueryWrapper` 对象,而是在序列化之前将其转换为可序列化的数据。 示例:自定义编码器(仅当你能将 `AsyncQueryWrapper` 转换为同步可获取的数据时) - 如果 `AsyncQueryWrapper` 有同步的求值方法(通常没有,因为它是异步的),则可以在编码器中使用。但在大多数情况下,异步对象应该在序列化之前就处理完毕。 所以,我们更倾向于在数据传递给序列化器之前,就将其转换为可序列化的形式。 具体到你的错误,你应该检查在试图序列化的对象中哪里包含了 `AsyncQueryWrapper`,然后将其替换为实际的值。 例如: 假设你有一个字典包含 `AsyncQueryWrapper`: data = { 'name': 'Test', 'results': AsyncQueryWrapper(...) # 这里导致了错误 } 你需要在序列化之前将 'results' 的值替换为实际查询结果(可能需要使用await): data['results'] = await data['results'] # 假设可以这样等待 但是,请注意:在普通的同步函数中无法使用 `await`。因此,如果你的代码在异步环境中(比如异步视图),你应该在视图内等待所有异步操作完成后再序列化。 总结步骤: 1. 确定在序列化数据结构中哪个地方包含了 `AsyncQueryWrapper` 对象。 2. 在序列化之前,使用 `await` 获取该异步查询的实际结果(注意:只能在异步函数中使用await)。 3. 将查询结果转换为可序列化的类型(如列表、字典等)。 4. 如果使用框架(如Django Rest Framework、Sanic、FastAPI等),检查是否有内置的异步序列化方式。 示例(异步视图): ```python from django.http import JsonResponse async def my_view(request): # 获取异步查询集 async_query = MyModel.objects.filter(...) # 执行异步查询,获取结果列表 results = await async_query # 将每个结果转换为字典(假设模型有to_dict方法) data = [obj.to_dict() for obj in results] return JsonResponse(data, safe=False) ``` 如果你的项目使用的是Tornado(从问题中的路径推测),那么可以这样处理: ```python import json from tornado.web import RequestHandler class MyHandler(RequestHandler): async def get(self): # 获取异步查询集(假设使用TortoiseORM) objects = await MyModel.all() # 转换为可序列化的列表 data = [] for obj in objects: # 使用TortoiseORM的to_dict方法 data.append(await obj.to_dict()) self.write(json.dumps(data)) ``` 注意:TortoiseORM的 `to_dict` 方法可能是异步的(如果包含关系字段),所以需要使用 `await`。如果是普通字段,则可能是同步的。 如果以上方法不能直接解决,请提供更多上下文,例如: - 你使用的ORM是什么? - 你是在什么框架下运行(Tornado?Django?FastAPI?)? - 错误发生的代码片段。 由于你没有提供具体的代码,以上是通用的解决方案。 相关问题: 1. 如何将TortoiseORM模型实例转换为可序列化的字典? 2. 在Tornado异步视图中如何处理数据库查询? 3. 如何设计一个自定义的JSON编码器来处理自定义类型? 4. 异步编程中,如何避免在序列化时出现未求值的异步对象? 5. Django REST Framework如何支持异步视图? 引用: [^1]: 这个错误通常发生在尝试将一个非 JSON 可序列化的对象转换为 JSON 字符串时。 [^2]: 在响应中包含了一个 Decimal 类型的对象,而 JSON 序列化器不知道如何处理这种类型。 [^3]: 问题在于您尝试将一个bytes类型的对象作为JSON数据传递给requests.post方法的json参数。 </think>### 解决 `TypeError: Object of type AsyncQueryWrapper is not JSON serializable` 错误 这个错误发生在尝试将 `AsyncQueryWrapper` 类型对象直接序列化为 JSON 格式时。以下是完整的解决方案: #### 1. 理解错误根源 - **问题本质**:Python 的 `json` 模块只能序列化基本数据类型(dict/list/str/int/float/bool/None) - **AsyncQueryWrapper** 是异步查询的结果包装器(常见于 ORM 如 Django、TortoiseORM 或 SQLAlchemy),包含数据库查询状态而非实际数据 - 错误触发点通常是: ```python # 错误示例 import json json.dumps(AsyncQueryWrapper_object) # 触发错误 ``` #### 2. 解决方案(根据使用场景选择) ##### 方法一:执行异步查询获取实际数据 ```python from models import YourModel # 替换为实际模型 async def your_view(): # 错误做法 ❌ # queryset = YourModel.objects.filter(...) # 正确做法 ✅ results = await YourModel.objects.filter(...) # 添加 await 执行查询 data = [{"id": obj.id, "name": obj.name} for obj in results] # 转换为可序列化格式 return json.dumps(data) # 成功序列化 ``` ##### 方法二:自定义 JSON 编码器 ```python import json from json import JSONEncoder class AsyncQueryEncoder(JSONEncoder): def default(self, obj): # 识别 AsyncQueryWrapper 类型 if type(obj).__name__ == "AsyncQueryWrapper": return list(obj) # 转换为可序列化的列表 return super().default(obj) # 使用自定义编码器 data = {"result": async_query_wrapper} json_str = json.dumps(data, cls=AsyncQueryEncoder) ``` ##### 方法三:ORM 内置序列化方法 - **Django ORM**: ```python from django.core import serializers data = serializers.serialize('json', await queryset) ``` - **TortoiseORM**: ```python from tortoise.contrib.sanic import register_tortoise data = [await obj.to_dict() for obj in await queryset] ``` - **SQLAlchemy**: ```python data = [dict(row) for row in await session.execute(query)] ``` #### 3. 完整工作流示例(Tornado + TortoiseORM) ```python import json from tornado.web import RequestHandler from models import User class UserHandler(RequestHandler): async def get(self): # 执行异步查询获取数据 users = await User.all() # 转换为可序列化格式 user_data = [ {"id": u.id, "name": u.name, "email": u.email} for u in users ] # 返回 JSON 响应 self.write(json.dumps(user_data)) ``` #### 4. 调试技巧 1. 检查对象类型: ```python print(type(obj)) # 输出: <class 'your_module.AsyncQueryWrapper'> ``` 2. 尝试手动转换: ```python print(list(obj)[:1]) # 尝试获取第一条数据测试 ``` 3. 使用安全序列化方法: ```python def safe_serialize(obj): try: return json.dumps(obj) except TypeError: return str(obj) # 回退方案 ``` #### 5. 预防措施 - **避免直接序列化**:永远不要将查询集/包装器直接传递给 `json.dumps()` - **明确数据转换**:在视图层完成数据到基本类型的转换 - **使用 ORM 工具**:优先使用 ORM 提供的 `.values()` 或 `.to_dict()` 方法 - **异步操作规范**:确保所有异步调用使用 `await` 获取实际结果 > 关键原则:JSON 序列化前必须将异步对象 **转换为基本数据类型**。数据库查询包装器需要显式执行才能获取实际数据[^1]。 --- ### 相关问题 1. 如何在 FastAPI 中正确处理异步数据库查询的序列化? 2. 如何为自定义类型创建通用的 JSON 序列化方案? 3. 同步 ORM (如 Django ORM) 和异步 ORM (TortoiseORM) 在序列化时的主要区别是什么? 4. JSON 序列化时遇到 `Object of type datetime is not JSON serializable` 应如何解决? 5. 如何设计 API 返回格式以确保所有数据类型都能正确序列化? [^1]: 这个错误通常发生在尝试将一个非 JSON 可序列化的对象转换为 JSON 字符串时
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

青塬科技

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值