nikkie-ftnextの日記

イベントレポートや読書メモを発信

Pydantic の BaseModel を継承したクラスの model_dump と model_dump_json

結論

BaseModelを継承したクラスのインスタンス


はじめに

七尾百合子さん、お誕生日 305日目 おめでとうございます! nikkieです。

Pydantic について Today(※最近) I Learned です

目次

辞書に変換するか、JSON 文字列に変換するか

model_dump()

https://docs.pydantic.dev/latest/api/base_model/#pydantic.BaseModel.model_dump

Generate a dictionary representation of the model,

Serializing data - Python mode
https://docs.pydantic.dev/latest/concepts/serialization/#python-mode

When using the Python mode, Pydantic models (略) will be (recursively) converted to dictionaries.
This is achievable by using the model_dump() method:

✍️Pydantic モデル(例:BaseModelを継承したクラス)は、model_dump()メソッドで再帰的に辞書に変換される

model_dump_json()

https://docs.pydantic.dev/latest/api/base_model/#pydantic.BaseModel.model_dump_json

Generates a JSON representation of the model using Pydantic's to_json method.

Serializing data - JSON mode
https://docs.pydantic.dev/latest/concepts/serialization/#json-mode

Pydantic allows data to be serialized directly to a JSON-encoded string, by trying its best to convert Python values to valid JSON data.
This is achievable by using the model_dump_json() method:

✍️JSON エンコード文字列に直接シリアライズされる

model_dump()mode引数

model_dump(
    *,
    mode: Literal["json", "python"] | str = "python",
    # 省略
) -> dict[str, Any]

If mode is 'json', the output will only contain JSON serializable types.
If mode is 'python', the output may contain non-JSON-serializable Python objects.

  • mode引数を"json"と指定した時、JSON シリアライズ可能な型のみを含む
  • mode引数を"python"と指定した時、JSON シリアライズできない Python オブジェクトを含むかもしれない
    • mode引数のデフォルト値はこちら

例えば Enum の扱いが違いました。
ADK の EvalStatus という Enum で示します:https://github.com/google/adk-python/blob/v1.22.1/src/google/adk/evaluation/eval_metrics.py#L37-L40

class EvalStatus(Enum):
  PASSED = 1
  FAILED = 2
  NOT_EVALUATED = 3

mode="json"では、Enum は value に変換されています。

{'eval_case_results': [{'final_eval_status': 1}]}

mode="python"では、Enum はそのまま保持されます。

{'eval_case_results': [{'final_eval_status': <EvalStatus.PASSED: 1>}]}

経緯:adk-python に取り違えたバグがあったのです

adk-python 1.22.0 で、私の PR とは別のコミットにより直っています
https://github.com/google/adk-python/commit/fc4e3d6f607032259e68e91bcb1ad0815a03164e

eval_set_result_json = eval_set_result.model_dump_json()
with open(eval_set_result_file_path, "w", encoding="utf-8") as f:
  f.write(json.dumps(eval_set_result_json, indent=2))

model_dump_json()では JSON 形式の文字列を得るため、json.dumps()1で文字列リテラルJSON にしてしまっています(=バグ2

取り組んでみたところ、eval_set_result.model_dump(mode="json")とすることで、JSON シリアライズ可能な辞書を得ます。
この辞書をjson.dumps()すれば JSON 形式の文字列となりますね3

引数なくmodel_dump()したことで Enum の差でテストが落ち、mode引数を知るのにつながりました。

終わりに

Pydantic のBaseModelを継承したクラスは、辞書にしたければmodel_dump()JSON 文字列にしたければmodel_dump_json()です。
model_dump()mode引数で JSON シリアライズ可能な型のみとするかを指定できます(指定しないときは JSON シリアライズできない型も含みます)


  1. json.dumps()json.dump()string の s が付いたとおぼえています。JSON 形式の文字列を返す
  2. 2重でエンコードする実装になっていた」ADKで評価 素振りの記:評価における Agent の応答や評価モデルの応答をファイルに保存する - nikkie-ftnextの日記
  3. 記事を執筆していてf.write(json.dumps(...))は回りくどく、json.dump()で済むように思われました