概要
過去 に Pydantic V1 で検討しましたが V2 でも検討してみます
ydantic-xml が V1 にしか対応していないので pydantic-xml を使わない方針で作成してみます
環境
- Python 3.11.3
- fastapi 0.100.1
- pypdantic 2.1.1
方針
- Xml レスポンスは fastapi のレスポンスクラスを継承して作成
- Xml 用のモデル定義は pydantic の BaseModel を継承する
- レスポンスを Xml に変換するためのシリアライザを持たせる
- Xml フィールドのキーはパスカルケースに変換する
サンプルコード
import textwrap
from typing import LiteralString, Union
import dicttoxml
from fastapi import FastAPI, Response
from pydantic import BaseModel, ConfigDict, Field
from pydantic.alias_generators import to_pascal
app = FastAPI()
class User(BaseModel):
# alias_generator を指定することで Xml のキー情報をパスカルケースにする
model_config = ConfigDict(populate_by_name=True, alias_generator=to_pascal)
name: str = Field()
age: int = Field()
# model_dump (dict) を xml (str) に変換する
def to_xml(self) -> LiteralString | bytes:
return dicttoxml.dicttoxml(
self.model_dump(by_alias=True),
custom_root=to_pascal("some_custom_root"),
attr_type=False,
)
# fastapi で Xml レスポンスを生成するためのクラス
class XmlResponse(Response):
def __init__(self, content: Union[str, bytes], status_code: int = 200):
super().__init__(content, status_code=status_code, media_type="application/xml")
@app.get(
"/",
response_class=XmlResponse,
response_model=User,
# openapi-doc はここで生成する
responses={
200: {
"description": "Success",
"content": {
"application/xml": {
"schema": {"$ref": "#/components/schemas/User"},
"example": textwrap.dedent(
"""
<?xml version="1.0" encoding="UTF-8"?>
<User>
<Name>hawk</Name>
<Age>20</Age>
</User>
"""
)[1:-1],
},
"application/json": None,
},
},
},
)
def xml():
result = User(name="hawksnowlog", age=10)
return XmlResponse(content=result.to_xml())
動作確認
- curl localhost:8000
<?xml version="1.0" encoding="UTF-8" ?><SomeCustomRoot><Name>hawksnowlog</Name><Age>10</Age></SomeCustomRoot>
localhost:8000/docs
最後に
pydantic V2 で Xml レスポンスを表現する方法を検討してみました
to_xml などはシリアライザを指定して自動で呼び出してくれるようになるともっといい感じなるかなと思います
0 件のコメント:
コメントを投稿