スクリプトのお勉強

小ネタ: Python のdataclass でNone or “”を使わないJSONデータを定義する

投稿日:2022年12月18日 更新日:

PythonをREST APIのクライアントとして作成する際、dataclassesを使用しようと思いました。型が見た瞬間分かるし、しっかりしているからです。

でも、そのREST API は””やNoneのデータを受け付けない仕様でした。なので、JSONとして送信する際は、削除する必要があります。

作成に何故か苦労したので、その方法を書いておきます。

環境

  • WSL2(ubuntu 22.04 LTS)
  • Python: 3.10.4

dataclassとは

「dataclassとはデータを保持のためのclassである」とのことです。

ただ、実はそれほどdataclassには思い入れがなく、一つ前はgolangを書いていたので、当然データを先に定義するだろうと思いこんで使っていたに過ぎません。

でもまぁ先にデータ定義しても悪いことはないので、使ってみようと思いました。

Noneや””が入っていていてはいけない

WebUIのFormからvalidateした結果、入力しないデータは””で入ってきます。もちろん必須であれば””をvalidateで弾いてもいいですが、Optionalな値だとします。

そして、Formの内容を結果的にJSONとして送信するのですが、サーバー側の仕様で、Noneや””が入ってるとinvalidでエラーになります。

サーバ側を変えてもらってもいいですが、送信すること自体意味がないのでクライアント側から送信しない方針がいいだろうと考えました。ここらへんはいろいろな事情によるでしょうけど。

意外とやりにくかった

簡単に実装できるかなと思ったのですが、意外に方法がわからず四苦八苦しました。

結局こうなった

サンプルコードは以下です。

from dataclasses import asdict, dataclass
from typing import List, Optional


@dataclass
class Five():
    data3: Optional[str] = None
    data4: Optional[str] = None

@dataclass
class Four():
    five: List[Five]
    data1: Optional[str] = None
    data2: Optional[str] = None

@dataclass
class Three():
    four: Four

@dataclass
class Two():
    data_test1: Optional[str] = None
    data_test2: Optional[str] = None

@dataclass
class Top():
    two: Two
    three: Three

# Dictで設定する方法
inited = {
    "two": {
        "data_test1": "data_test1"
    },
    "three": {
        "four": {
            "five": [{
                "data3": "data3"
            }],
            "data2": "data2"
        }
    }
}

instance = Top(**inited)
print(repr(instance))
print(asdict(instance))

# dataclassesだけで実現する方法
top = Top(two=Two(data_test1="data_test1", data_test2=""), three=Three(four=Four(five=[Five(data3="data3")], data2="data2")))
print(repr(top))
# None or ""の場合は辞書から除く
print(asdict(top, dict_factory=lambda tuples: {tuple[0]: tuple[1] for tuple in tuples if tuple[1]}))

実行結果

上記を実行した結果は以下のとおりです。

op(two={'data_test1': 'data_test1'}, three={'four': {'five': [{'data3': 'data3'}], 'data2': 'data2'}})
{'two': {'data_test1': 'data_test1'}, 'three': {'four': {'five': [{'data3': 'data3'}], 'data2': 'data2'}}}
Top(two=Two(data_test1='data_test1', data_test2=''), three=Three(four=Four(five=[Five(data3='data3', data4=None)], data1=None, data2='data2')))
{'two': {'data_test1': 'data_test1'}, 'three': {'four': {'five': [{'data3': 'data3'}], 'data2': 'data2'}}}

主なところ

実現する上で、主なところは以下です。

  • フィールドをOptional[str] = Noneにする
  • dict_factoryを指定してNoneや””を無視する

いろいろ検索しましたが、あまりちゃんと載っておらず、苦労しました。

上記でやりたいことができました。

参考

  • https://gist.github.com/xhiroga/6c5a8ac4749d25f5ecbffcfad9e750e4
    まんまのスニペット。
  • https://www.lifewithpython.com/2022/09/python-dataclasses-dict-factory.html
    dict_factoryが使えることに気がついたブログ

-スクリプトのお勉強
-,

執筆者:

関連記事

svelteをチュートリアルしてみる

年末の休みは、svelteのチュートリアルをしてみました。なんとなくやはりそう?なので。 svelteとは Webアプリのフロントエンド(UI)用フレームワークです。要するにReactみたいなもんです …

Pythonからsvnしてみる

SVN: レガシーシステムの一つ。。 私の関わってるシステムでは、いまだにsvnレポジトリがあります。pythonで処理する必要性が生じましたが、そういえばどう処理していいもんか、完全に忘れました。 …

Djangoアプリサンプル – 画像ファイルアップロード + 顔モザイク(画像アップロード編)

仕事で使用している、Djangoについて、サンプルを作成してみようと思います。ここでは、画像ファイルのアップロードと顔モザイクを行うwebアプリを作成してみようと思います。 この記事ではファイルアップ …

Nuxt.jsのFormで入力/確認/完了フォームを作成してみた(その2)

前回の記事の続きです。以下について書いていきます。この記事で終わりのはず? バリデーション機能(主にIPアドレスのバリデーション追加)画面 バリデーション機能 バリデーション機能は、vee-valid …

CentOS7をRocky Linux 9に移行する

このVMのOSをCentOS7にした関係上、そろそろupdateする必要があります。そもそもCentOS7を選択した理由は、仕事で使用するからでした。 で、仕事ではRocky Linux 9なので、こ …

google オプトアウト Click here to opt-out.