はじめに
n番煎じ感が否めませんが、Pythonでjsonを扱う際にdataclass
×dataclasses-json
の組み合わせが便利だったので紹介します。
結局何が便利?
- JSONとの変換がto_json()メソッドとfrom_json()メソッドだけで行える
- to_dict()メソッドとfrom_dict()メソッドにより辞書型に変換することも可能です
- ネストされたクラスでも下の階層まで各クラスに展開してくれる
- スネークケースとキャメルケース(やパスカルケースなど)を自動的に変換してくれる
- Pythonはスネークケース、jsonはキャメルケース、というパターンが多いため、この機能によって命名規則に困らなくなります
余談
後から知ったのですが、Pydanticでも同様の機能が提供されているようです準備
環境
今回テストした環境は以下の通りです
- Python 3.12.0
- dataclasses-json 0.6.7
dataclasses-jsonはpip
でインストールすることが可能です
$ pip install dataclasses-json
dataclass自体に関しては既に多くの記事で詳しく紹介されているため、本記事では特に取り扱いません
実践
クラスの用意
今回は以下のようなネストされたFamilyクラスを使用して具体的に見ていきます
変数名はスネークケースで宣言されていますが、クラスの定義の前に@dataclass_json(letter_case=LetterCase.CAMEL)
デコレータがついています
このデコレータの引数で記法を指定することによって自動的にスネークケースとキャメルケースを変換してくれます
from dataclasses import dataclass
from dataclasses_json import dataclass_json, LetterCase
@dataclass_json(letter_case=LetterCase.CAMEL)
@dataclass
class Address:
postal_code: str
country: str
city: str
street: str
@dataclass_json(letter_case=LetterCase.CAMEL)
@dataclass
class Person:
first_name: str
last_name: str
age: int
address: Address
@dataclass_json(letter_case=LetterCase.CAMEL)
@dataclass
class Family:
members: list[Person]
デコレータのLetterCaseについて
CAMELケース以外にも、SNAKE、KEBAB、PASCALが用意されているようです
※何も指定しなかった場合は変数名に使用しているケースがそのまま適用されます
to_json
オブジェクトをJSONに変換するにはdataclass_jsonで追加される.to_json()メソッドを使用します
family_obj = Family(
members=[
Person(
first_name='hoge',
last_name='fuga',
age=20,
address=Address(
postal_code='100-0000',
country='Japan',
city='Tokyo',
street='hoge street'
)
),
Person(
first_name='foo',
last_name='bar',
age=30,
address=Address(
postal_code='200-0000',
country='Japan',
city='Osaka',
street='foo street'
)
)
]
)
json_str = family_obj.to_json(indent=4)
print(json_str)
# output
# {
# "members": [
# {
# "firstName": "hoge",
# "lastName": "fuga",
# "age": 20,
# "address": {
# "postalCode": "100-0000",
# "country": "Japan",
# "city": "Tokyo",
# "street": "hoge street"
# }
# },
# {
# "firstName": "foo",
# "lastName": "bar",
# "age": 30,
# "address": {
# "postalCode": "200-0000",
# "country": "Japan",
# "city": "Osaka",
# "street": "foo street"
# }
# }
# ]
# }
to_jsonの引数でindentを4に指定することで整形して出力してくれます
(この辺りはjsonモジュールのdumpsと同様ですね)
注目すべきは出力されたJSON文字列のキー名がキャメルケースになっていることです
先ほど、クラスの定義時にデコレータでキャメルケースを指定しておいたことで、自動的に変換された状態でJSON文字列になってくれています
これでデフォルトの命名規則がキャメルケースのTypeScriptなどとJSONでやり取りする時も困りませんね
from_json
逆にJSON文字列からオブジェクトに変換する際はfrom_dict()メソッドを使用します
new_json_str = '''
{
"members": [
{
"firstName": "hogehoge",
"lastName": "fugafuga",
"age": 30,
"address": {
"postalCode": "300-0000",
"country": "Japan",
"city": "Fukuoka",
"street": "hogehoge street"
}
},
{
"firstName": "foobar",
"lastName": "barfoo",
"age": 40,
"address": {
"postalCode": "400-0000",
"country": "Japan",
"city": "Nagoya",
"street": "foobar street"
}
}
]
}
'''
new_family_obj = Family.from_json(new_json_str)
print(new_family_obj)
# output
# Family(
# members=[
# Person(
# first_name='hogehoge', last_name='fugafuga', age=30,
# address=Address(postal_code='300-0000', country='Japan', city='Fukuoka', street='hogehoge street')
# ),
# Person(
# first_name='foobar', last_name='barfoo', age=40,
# address=Address(postal_code='400-0000', country='Japan', city='Nagoya', street='foobar street')
# )
# ]
# )
新しくnew_json_strを用意し、クラス名.from_jsonの形で呼び出します
今回はJSON文字列⇒オブジェクトなので、キャメルケース⇒スネークケースに自動的に変換されていますね
ちなみに、JSON文字列内のキーにスネークケースが含まれていても問題なく変換可能でした
to_dict
ここまではオブジェクトとJSONとの変換でしたが、dataclasses-jsonでは辞書型との変換もサポートされています
まずはオブジェクトを辞書型に変換するto_dict()メソッドです
print(family_obj)
# output
# Family(
# members=[
# Person(
# first_name='hoge', last_name='fuga', age=20,
# address=Address(postal_code='100-0000', country='Japan', city='Tokyo', street='hoge street')
# ),
# Person(
# first_name='foo', last_name='bar', age=30,
# address=Address(postal_code='200-0000', country='Japan', city='Osaka', street='foo street')
# )
# ]
# )
family_dict = family_obj.to_dict()
print(family_dict)
# output
# {
# 'members': [
# {
# 'firstName': 'hoge', 'lastName': 'fuga', 'age': 20,
# 'address': {'postalCode': '100-0000', 'country': 'Japan', 'city': 'Tokyo', 'street': 'hoge street'}
# },
# {
# 'firstName': 'foo', 'lastName': 'bar', 'age': 30,
# 'address': {'postalCode': '200-0000', 'country': 'Japan', 'city': 'Osaka', 'street': 'foo street'}
# }
# ]
# }
from_dict
from_dict()メソッドで辞書型からオブジェクトに変換できます
new_family_dict = {
'members': [
{
'firstName': 'hogehoge', 'lastName': 'fugafuga', 'age': 30,
'address': {'postalCode': '300-0000', 'country': 'Japan', 'city': 'Fukuoka', 'street': 'hogehoge street'}
},
{
'firstName': 'foobar', 'lastName': 'barfoo', 'age': 40,
'address': {'postalCode': '400-0000', 'country': 'Japan', 'city': 'Nagoya', 'street': 'foobar street'}
}
]
}
new_family_obj = Family.from_dict(new_family_dict)
print(new_family_obj)
# output
# Family(
# members=[
# Person(
# first_name='hogehoge', last_name='fugafuga', age=30,
# address=Address(postal_code='300-0000', country='Japan', city='Fukuoka', street='hogehoge street')
# ),
# Person(
# first_name='foobar', last_name='barfoo', age=40,
# address=Address(postal_code='400-0000', country='Japan', city='Nagoya', street='foobar street')
# )
# ]
# )
from_dict()メソッドでもfrom_json()メソッドと同様に、元の辞書型がキャメルケースではなくスネークケースでも問題なく変換できました
まとめ
- dataclass定義時にdataclass_jsonデコレータを付与する
- 引数のletter_caseで変換時の記法を指定できる
-
オブジェクトから変換
- to_json()メソッドやto_dict()メソッド
-
オブジェクトに変換
- from_json()メソッドやfrom_dict()メソッド