Moh*_*dar 12 python serialization json fastapi
我在FastAPI和Flask中编写了具有相同功能的相同 API 应用程序。但是,当返回 JSON 时,两个框架之间的数据格式不同。两者都使用相同的json库,甚至相同的代码:
import json
from google.cloud import bigquery
bigquery_client = bigquery.Client()
@router.get('/report')
async def report(request: Request):
response = get_clicks_impression(bigquery_client, source_id)
return response
def get_user(client, source_id):
try:
query = """ SELECT * FROM ....."""
job_config = bigquery.QueryJobConfig(
query_parameters=[
bigquery.ScalarQueryParameter("source_id", "STRING", source_id),
]
)
query_job = client.query(query, job_config=job_config) # Wait for the job to complete.
result = []
for row in query_job:
result.append(dict(row))
json_obj = json.dumps(result, indent=4, sort_keys=True, default=str)
except Exception as e:
return str(e)
return json_obj
Run Code Online (Sandbox Code Playgroud)
Flask中返回的数据是dict:
{
"User": "fasdf",
"date": "2022-09-21",
"count": 205
},
{
"User": "abd",
"date": "2022-09-27",
"count": 100
}
]
Run Code Online (Sandbox Code Playgroud)
而在FastAPI中是字符串:
"[\n {\n \"User\": \"aaa\",\n \"date\": \"2022-09-26\",\n \"count\": 840,\n]"
Run Code Online (Sandbox Code Playgroud)
我使用的原因json.dumps()是它date不能被打扰。
Chr*_*ris 16
如果您在返回对象之前序列化该对象,请使用json.dumps()(如示例中所示),例如:
import json\n\n@app.get(\'/user\')\nasync def get_user():\n return json.dumps(some_dict, indent=4, default=str)\nRun Code Online (Sandbox Code Playgroud)\n返回的 JSON 对象最终将被序列化两次,因为 FastAPI 会在幕后自动序列化返回值。因此,您最终得到输出字符串的原因是:
\n"[\\n {\\n \\"User\\": \\"aaa\\",\\n \\"date\\": \\"2022-09-26\\",\\n ... \nRun Code Online (Sandbox Code Playgroud)\n查看可用的解决方案,以及下面关于 FastAPI/Starlette 如何在后台工作的说明。
\n第一个选项是照常返回数据(例如dict、list等)\xe2\x80\x94,即使用return some_dict\xe2\x80\x94 和 FastAPI,在幕后自动将该返回值转换为 JSON,首先将数据转换为 JSON 兼容的数据后,使用jsonable_encoder. 确保jsonable_encoder 将不可序列化的对象(例如datetime对象)转换为str. 然后,FastAPI 会将 JSON 兼容的数据放入 a 中,这将向客户端JSONResponse返回编码响应(这也在本答案的选项 1 中进行了解释)。正如Starlette 的源代码中所示,将使用 Python 标准来序列化(对于 alternatvie/更快的 JSON 编码器,请参阅此答案和此答案)。application/jsonJSONResponsejson.dumps()dict
from datetime import date\n\n\nd = [\n {"User": "a", "date": date.today(), "count": 1},\n {"User": "b", "date": date.today(), "count": 2},\n]\n\n\n@app.get(\'/\')\ndef main():\n return d\nRun Code Online (Sandbox Code Playgroud)\n上式等价于:
\nfrom fastapi.responses import JSONResponse\nfrom fastapi.encoders import jsonable_encoder\n\n@app.get(\'/\')\ndef main():\n return JSONResponse(content=jsonable_encoder(d))\nRun Code Online (Sandbox Code Playgroud)\n输出:
\n[{"User":"a","date":"2022-10-21","count":1},{"User":"b","date":"2022-10-21","count":2}]\nRun Code Online (Sandbox Code Playgroud)\n
\n直接返回一个JSONResponse或一个自定义Response(在下面的选项 2 中进行了演示),以及继承自的任何其他响应类(请参阅此处的ResponseFastAPI 文档,以及此处的Starlette 文档和响应的实现)此处),如果愿意的话,还允许指定一个custom 。FastAPI/Starlette 类的实现可以在这里找到,并且可以在此处看到可以使用的 HTTP 代码列表(而不是直接传递HTTP 响应状态代码) 。例子: status_codeJSONResponseint
from fastapi import status\nfrom fastapi.responses import JSONResponse\nfrom fastapi.encoders import jsonable_encoder\n\n@app.get(\'/\')\ndef main():\n return JSONResponse(content=jsonable_encoder(d), status_code=status.HTTP_201_CREATED)\nRun Code Online (Sandbox Code Playgroud)\n如果出于任何原因(例如,尝试强制使用某些自定义 JSON 格式),您必须在返回对象之前序列化对象,然后您可以直接返回自定义对象Response,如本答案中所述。根据文档:
\n\n当您直接返回时,
\nResponse其数据不会被验证、\n转换(序列化),也不会自动记录。
此外,如下所述:
\n\n\nFastAPI(实际上是 Starlette)将自动包含\nContent-Length 标头。它还将包括一个 Content-Type 标头,\n基于
\nmedia_type并附加文本类型的字符集。
因此,您还可以将 设定media_type为您期望数据的任何类型;在这种情况下,即application/json。下面给出示例。
注 1:本答案中发布的 JSON 输出(在选项 1 和选项 2 中)是直接通过浏览器访问 API 端点的结果(即,通过在浏览器的地址栏中键入 URL,然后按 Enter 键) )。如果您通过 Swagger UI 测试端点/docs,您会发现缩进不同(在两个选项中)。这是由于 Swagger UI 格式化application/json响应的方式造成的。如果您还需要在 Swagger UI 上强制自定义缩进,则可以避免在下面的示例中指定media_typefor 。Response这将导致将内容显示为text,因为Content-Type响应中会缺少标头,因此 Swagger UI 无法识别数据的类型,以便自定义它们的格式(在application/json响应的情况下)。
注 2:将default参数设置为strinjson.dumps()可以序列化date对象,否则如果未设置,您将得到:TypeError: Object of type date is not JSON serializable。这default是一个为无法序列化的对象调用的函数。它应该返回对象的 JSON 编码版本。在本例中str,它是 ,这意味着每个不可序列化的对象都会转换为字符串。如果您想以自定义方式序列化对象,您还可以使用自定义函数或JSONEncoder子类,如此处演示的那样。此外,正如前面选项 1 中提到的,可以使用替代 JSON 编码器,例如orjson,与标准库相比,这可能会提高应用程序的性能json(请参阅此答案和此答案)。
注 3:FastAPI/Starlette 接受a或对象Response作为参数。如此处的实现所示,如果您不传递对象,Starlette 将尝试使用 对其进行编码。因此,例如,如果您传递了 a ,您将得到:。在下面的示例中,传递了一个 JSON,稍后将对其进行编码(您也可以在将其传递给对象之前自行对其进行编码)。contentstrbytesbytescontent.encode(self.charset)dictAttributeError: \'dict\' object has no attribute \'encode\'strbytesResponse
from fastapi import Response\nfrom datetime import date\nimport json\n\n\nd = [\n {"User": "a", "date": date.today(), "count": 1},\n {"User": "b", "date": date.today(), "count": 2},\n]\n\n\n@app.get(\'/\')\ndef main():\n json_str = json.dumps(d, indent=4, default=str)\n return Response(content=json_str, media_type=\'application/json\')\nRun Code Online (Sandbox Code Playgroud)\n输出:
\n[\n {\n "User": "a",\n "date": "2022-10-21",\n "count": 1\n },\n {\n "User": "b",\n "date": "2022-10-21",\n "count": 2\n }\n]\nRun Code Online (Sandbox Code Playgroud)\n
| 归档时间: |
|
| 查看次数: |
22418 次 |
| 最近记录: |