どこから見てもメンダコ

軟体動物門頭足綱八腕類メンダコ科

Dataclassをjson形式でシリアライズ

Data ClassesはPython3.7からの新機能です。その名の通りデータを保持するためのクラスを簡潔に記述することができます。

Dataclassはdataclasses_jsonパッケージを使うことによりお手軽にjson形式へ変換できます。

github.com

json形式でシリアライズできると人間が直接編集したり非Pythonの外部アプリケーションにデータを渡したりする際にとても都合がよいです。

※追記:2022年現在ではpydantic.dataclasses.dataclassも有力な選択肢です

pydantic-docs.helpmanual.io

[目次]


インストール

pip install dataclasses_json

シンプルなDataclassの場合

PermissionConfigは何かのアプリケーションの権限設定をイメージしたサンプルクラスです。

dataclasses_jsonモジュールを Dataclassへ適用するには通常の@dataclassデコレータに@dataclass_jsonデコレータを重ねるだけでOKです。

from datetime import datetime
from dataclasses import dataclass
from typing import List, Dict
from uuid import uuid4, UUID

from dataclasses_json import dataclass_json


@dataclass_json
@dataclass
class PermissionConfig:
    uid : UUID = uuid4()
    updated: datetime = datetime.now()
    owner: str = None
    users : List[str] = None
    user_group: Dict[str, List[str]] = None
    note: str = "ensure_asciiをFalseにしないと日本語は文字化けする"
         

datetime.datetime型やuuid.UUID型にも対応しているのが嬉しいところです。 ではこのクラスへ具体的なデータを格納していきます。

>>> config = PermissionConfig()
>>> config.owner = "Alice"
>>> config.users = ["Bob", "Carol", "Dave", "Eve"]
>>> config.user_group = {"admin": ["Alice", "Bob"], "user":["Carol", "Dave", "Eve"]}
>>> print(config)
PermissionConfig(uid=UUID('f429b01b-3d72-416e-8909-9370b4a34770'), updated=datetime.datetime(2019, 11, 24, 3, 10, 13, 453036), owner='Alice', users=['Bob', 'Carol', 'Dave', 'Eve'], user_group={'admin': ['Alice', 'Bob'], 'user': ['Carol', 'Dave', 'Eve']}, note='ensure_asciiをFalseにしないと日本語は文字化けする')

ここまでは普通にDataclassですね。Json形式への変換は以下のように行います。

>>> config_json = config.to_json(indent=4, ensure_ascii=False)
>>> print(type(config_json)
<class 'str'>
>>> print(config_json)
{
    "uid": "f429b01b-3d72-416e-8909-9370b4a34770",
    "updated": 1574532613.453036,
    "owner": "Alice",
    "users": [
        "Bob",
        "Carol",
        "Dave",
        "Eve"
    ],
    "user_group": {
        "admin": [
            "Alice",
            "Bob"
        ],
        "user": [
            "Carol",
            "Dave",
            "Eve"
        ]
    },
    "note": "ensure_asciiをFalseにしないと日本語は文字化けする"
}

ensure_ascii=Falseにしないと日本語が文字化けすることに注意。

さらにこのJson形式文字列からPermissionConfigを復元します。

>>> config_from_json = PermissionConfig.from_json(setting_json)
>>> print(config_from_json)
PermissionConfig(uid=UUID('c6c0df3d-f702-436a-830a-5b51ec3c909b'), updated=datetime.datetime(2019, 11, 24, 1, 56, 25, 67129, tzinfo=datetime.timezone(datetime.timedelta(seconds=32400), '???? (?W?\x80??)')), owner='Alice', users=['Bob', 'Carol', 'Dave', 'Eve'], user_group={'admin': ['Bob', 'Alice'], 'user': ['Carol', 'Dave', 'Eve']}, note='')

正しく型を復元できているか確認します。

>>>print(type(config_from_json.uid),
           type(config_from_json.updated),
           type(config_from_json.owner),
           type(config_from_json.users),
           type(config_from_json.user_group))
<class 'uuid.UUID'> <class 'datetime.datetime'> <class 'str'> <class 'list'> <class 'dict'>

完璧ですね。

ネストしたDataclassの場合

Dataclassを保持するDataclassも使用可能です。

以下は先ほどのPermissionConfigPersonデータクラスを保持する例です。

@dataclass_json
@dataclass
class Person:
    name: str
    age: int


@dataclass_json
@dataclass
class PermissionConfig:
    uid : UUID = uuid4()
    updated: datetime = datetime.now()

    owner: Person = None
    users : List[Person] = None
    user_group: Dict[str, List[Person]] = None
    note: str = "ensure_asciiをFalseにしないと日本語は文字化けする"

先ほどと同様にデータを格納します。

>>> config = PermissionConfig()
>>> config.owner = Person(name="Alice", age=22)
>>> config.users = [Person(name="Bob", age=22), Person(name="Carol", age=35), Person(name="Dave", age=42), Person(name="Eve", age=27)]
>>> config.user_group = {"admin": [Person(name="Alice", age=22), Person(name="Bob", age=22)], "user":[Person(name="Carol", age=35), Person(name="Dave", age=42), Person(name="Eve", age=27)]}

>>> config_json = config.to_json(indent=4, ensure_ascii=False)
>>> print(type(config_json))
{
    "uid": "d683282a-0cb1-455f-a822-9d9d3cbca3f1",
    "updated": 1574533718.833665,
    "owner": {
        "name": "Alice",
        "age": 22
    },
    "users": [
        {
            "name": "Bob",
            "age": 22
        },
        {
            "name": "Carol",
            "age": 35
        },
        {
            "name": "Dave",
            "age": 42
        },
        {
            "name": "Eve",
            "age": 27
        }
    ],
    "user_group": {
        "admin": [
            {
                "name": "Alice",
                "age": 22
            },
            {
                "name": "Bob",
                "age": 22
            }
        ],
        "user": [
            {
                "name": "Carol",
                "age": 35
            },
            {
                "name": "Dave",
                "age": 42
            },
            {
                "name": "Eve",
                "age": 27
            }
        ]
    },
    "note": "ensure_asciiをFalseにしないと日本語は文字化けする"
}

jsonへのシリアライズはOK。ではデシリアライズは?

>>> config_from_json = PermissionConfig.from_json(config_json)
>>> print(config_from_json)
PermissionConfig(uid=UUID('aa2f5ff0-eee8-4e20-b711-ead988d90844'), updated=datetime.datetime(2019, 11, 24, 2, 11, 55, 430387, tzinfo=datetime.timezone(datetime.timedelta(seconds=32400), '???? (?W?\x80??)')), owner=Person(name='Alice', age=22), users=[Person(name='Bob', age=22), Person(name='Carol', age=35), Person(name='Dave', age=42), Person(name='Eve', age=27)], user_group={'admin': [Person(name='Alice', age=22), Person(name='Bob', age=22)], 'user': [Person(name='Carol', age=35), Person(name='Dave', age=42), Person(name='Eve', age=27)]}, note='ensure_asciiをFalseにしないと日本語は文字化けする')

OK!!