【Python】クラスをJSONにシリアライズする方法

Pythonでデータを保存したり、他のシステムとデータをやり取りする際に、JSON(JavaScript Object Notation)という形式がよく使われます。

この記事では、Pythonの標準ライブラリを使って、PythonオブジェクトをJSON形式に変換(シリアライズ)したり、逆にJSON形式からPythonオブジェクトに戻す(デシリアライズ)方法をわかりやすく解説します。

特に、クラスのインスタンスをJSONにシリアライズする方法や、カスタムシリアライザの作成方法についても詳しく説明します。

初心者の方でも理解しやすいように、具体的なコード例を交えながら進めていきますので、ぜひ参考にしてください。

目次から探す

PythonでのJSONシリアライズ

PythonでJSONシリアライズを行う方法について解説します。

JSON(JavaScript Object Notation)は、データを交換するための軽量なフォーマットで、Pythonでも広く利用されています。

Pythonでは、標準ライブラリを使用して簡単にJSONシリアライズを行うことができます。

Pythonの標準ライブラリ

jsonモジュールの紹介

Pythonには、JSONデータを扱うための標準ライブラリとしてjsonモジュールが用意されています。

このモジュールを使用することで、PythonオブジェクトをJSON形式に変換(シリアライズ)したり、JSON形式のデータをPythonオブジェクトに変換(デシリアライズ)したりすることができます。

基本的な使い方

jsonモジュールの基本的な使い方を見てみましょう。

まずは、PythonオブジェクトをJSON形式に変換する方法です。

以下のコードは、Pythonの辞書型オブジェクトをJSON文字列に変換する例です。

import json
# Pythonの辞書型オブジェクト
data = {
    "name": "Alice",
    "age": 30,
    "city": "Tokyo"
}
# JSON文字列に変換
json_data = json.dumps(data, ensure_ascii=False)
print(json_data)

このコードを実行すると、以下のようなJSON文字列が出力されます。

{"name": "Alice", "age": 30, "city": "Tokyo"}

次に、JSON文字列をPythonオブジェクトに変換する方法です。

以下のコードは、JSON文字列をPythonの辞書型オブジェクトに変換する例です。

import json
# JSON文字列
json_data = '{"name": "Alice", "age": 30, "city": "Tokyo"}'
# Pythonの辞書型オブジェクトに変換
data = json.loads(json_data)
print(data)

このコードを実行すると、以下のような辞書型オブジェクトが出力されます。

{'name': 'Alice', 'age': 30, 'city': 'Tokyo'}

Pythonオブジェクトのシリアライズ

基本データ型のシリアライズ

Pythonの基本データ型(数値、文字列、リスト、辞書など)は、jsonモジュールを使用して簡単にJSON形式にシリアライズすることができます。

以下に、いくつかの基本データ型をJSON形式にシリアライズする例を示します。

import json
# 数値
number = 42
json_number = json.dumps(number)
print(json_number)  # 出力: 42
# 文字列
string = "Hello, World!"
json_string = json.dumps(string)
print(json_string)  # 出力: "Hello, World!"
# リスト
list_data = [1, 2, 3, 4, 5]
json_list = json.dumps(list_data)
print(json_list)  # 出力: [1, 2, 3, 4, 5]
# 辞書
dict_data = {"name": "Alice", "age": 30}
json_dict = json.dumps(dict_data, ensure_ascii=False)
print(json_dict)  # 出力: {"name": "Alice", "age": 30}

辞書やリストのシリアライズ

辞書やリストなどの複雑なデータ構造も、jsonモジュールを使用して簡単にJSON形式にシリアライズすることができます。

以下に、辞書やリストを含む複雑なデータ構造をJSON形式にシリアライズする例を示します。

import json
# 複雑なデータ構造
complex_data = {
    "name": "Alice",
    "age": 30,
    "children": [
        {"name": "Bob", "age": 5},
        {"name": "Charlie", "age": 3}
    ],
    "address": {
        "city": "Tokyo",
        "zipcode": "100-0001"
    }
}
# JSON文字列に変換
json_complex_data = json.dumps(complex_data, ensure_ascii=False, indent=4)
print(json_complex_data)

このコードを実行すると、以下のような整形されたJSON文字列が出力されます。

{
    "name": "Alice",
    "age": 30,
    "children": [
        {
            "name": "Bob",
            "age": 5
        },
        {
            "name": "Charlie",
            "age": 3
        }
    ],
    "address": {
        "city": "Tokyo",
        "zipcode": "100-0001"
    }
}

このように、jsonモジュールを使用することで、Pythonの基本データ型や複雑なデータ構造を簡単にJSON形式にシリアライズすることができます。

次のセクションでは、クラスをJSONにシリアライズする方法について詳しく解説します。

クラスをJSONにシリアライズする方法

クラスの定義

シンプルなクラスの例

まずは、シンプルなクラスを定義してみましょう。

以下の例では、Personという名前のクラスを作成します。

このクラスは名前と年齢を属性として持ちます。

class Person:
    def __init__(self, name, age):
        self.name = name
        self.age = age

クラスのインスタンス生成

次に、このクラスのインスタンスを生成してみます。

person = Person("Alice", 30)

このコードを実行すると、personという名前のPersonクラスのインスタンスが生成されます。

クラスのシリアライズ

json.dumps()の使用

Pythonの標準ライブラリであるjsonモジュールを使用して、クラスのインスタンスをJSONにシリアライズすることができます。

しかし、デフォルトの設定ではクラスのインスタンスを直接シリアライズすることはできません。

import json
person = Person("Alice", 30)
json_data = json.dumps(person)

このコードを実行すると、TypeError: Object of type Person is not JSON serializableというエラーが発生します。

これは、json.dumps()がクラスのインスタンスをシリアライズできないためです。

デフォルトのシリアライズ方法とその限界

デフォルトのシリアライズ方法では、基本的なデータ型(文字列、数値、リスト、辞書など)のみがサポートされています。

クラスのインスタンスをシリアライズするためには、カスタムシリアライザを作成する必要があります。

カスタムシリアライザの作成

カスタムシリアライザの必要性

クラスのインスタンスをJSONにシリアライズするためには、カスタムシリアライザを作成する必要があります。

これにより、クラスのインスタンスを辞書形式に変換し、その辞書をJSONにシリアライズすることができます。

defaultパラメータの使用方法

json.dumps()関数には、defaultというパラメータがあります。

このパラメータにカスタムシリアライザを指定することで、クラスのインスタンスをシリアライズすることができます。

カスタムシリアライザの実装例

以下に、カスタムシリアライザの実装例を示します。

import json
class Person:
    def __init__(self, name, age):
        self.name = name
        self.age = age
def person_serializer(obj):
    if isinstance(obj, Person):
        return {"name": obj.name, "age": obj.age}
    raise TypeError(f"Type {type(obj)} not serializable")
person = Person("Alice", 30)
json_data = json.dumps(person, default=person_serializer)
print(json_data)

このコードを実行すると、{"name": "Alice<", "age": 30}というJSON文字列が出力されます。

クラスの属性を辞書に変換する方法

クラスの属性を辞書に変換するためには、__dict__属性を利用する方法があります。

__dict__属性の利用

__dict__属性を利用すると、クラスのインスタンスの属性を辞書形式で取得することができます。

import json
class Person:
    def __init__(self, name, age):
        self.name = name
        self.age = age
person = Person("Alice", 30)
json_data = json.dumps(person.__dict__)
print(json_data)

このコードを実行すると、{"name": "Alice", "age": 30}というJSON文字列が出力されます。

複雑なクラスのシリアライズ

複雑なクラスのシリアライズには、カスタムシリアライザをさらに拡張する必要があります。

ネストされたオブジェクトのシリアライズ

クラスの属性に他のクラスのインスタンスが含まれている場合、そのインスタンスもシリアライズする必要があります。

import json
class Address:
    def __init__(self, city, street):
        self.city = city
        self.street = street
class Person:
    def __init__(self, name, age, address):
        self.name = name
        self.age = age
        self.address = address
def custom_serializer(obj):
    if isinstance(obj, Person):
        return {"name": obj.name, "age": obj.age, "address": obj.address}
    if isinstance(obj, Address):
        return {"city": obj.city, "street": obj.street}
    raise TypeError(f"Type {type(obj)} not serializable")
address = Address("Tokyo", "Shibuya")
person = Person("Alice", 30, address)
json_data = json.dumps(person, default=custom_serializer)
print(json_data)

このコードを実行すると、{"name": "Alice", "age": 30, "address": {"city": "Tokyo", "street": "Shibuya"}}というJSON文字列が出力されます。

再帰的なシリアライズ方法

再帰的なシリアライズ方法を使用すると、ネストされたオブジェクトを自動的にシリアライズすることができます。

import json
class Address:
    def __init__(self, city, street):
        self.city = city
        self.street = street
class Person:
    def __init__(self, name, age, address):
        self.name = name
        self.age = age
        self.address = address
def custom_serializer(obj):
    if hasattr(obj, "__dict__"):
        return obj.__dict__
    raise TypeError(f"Type {type(obj)} not serializable")
address = Address("Tokyo", "Shibuya")
person = Person("Alice", 30, address)
json_data = json.dumps(person, default=custom_serializer)
print(json_data)

このコードを実行すると、{"name": "Alice", "age": 30, "address": {"city": "Tokyo", "street": "Shibuya"}}というJSON文字列が出力されます。

この方法では、クラスのインスタンスが持つすべての属性を再帰的にシリアライズすることができます。

JSONデシリアライズ

デシリアライズの基本

json.loads()の使用

JSONデータをPythonオブジェクトに変換するためには、jsonモジュールのloads()関数を使用します。

この関数は、JSON形式の文字列を引数として受け取り、対応するPythonオブジェクトを返します。

import json
json_data = '{"name": "Alice", "age": 30, "city": "Tokyo"}'
python_obj = json.loads(json_data)
print(python_obj)
# 出力: {'name': 'Alice', 'age': 30, 'city': 'Tokyo'}

基本データ型のデシリアライズ

json.loads()は、基本的なデータ型(文字列、数値、リスト、辞書など)を適切にデシリアライズします。

以下にいくつかの例を示します。

# 数値のデシリアライズ
json_data = '123'
python_obj = json.loads(json_data)
print(python_obj)
# 出力: 123
# リストのデシリアライズ
json_data = '[1, 2, 3, 4, 5]'
python_obj = json.loads(json_data)
print(python_obj)
# 出力: [1, 2, 3, 4, 5]
# 辞書のデシリアライズ
json_data = '{"key1": "value1", "key2": "value2"}'
python_obj = json.loads(json_data)
print(python_obj)
# 出力: {'key1': 'value1', 'key2': 'value2'}

クラスのデシリアライズ

クラスインスタンスの再生成

クラスのインスタンスをJSONから再生成するためには、まずJSONデータを辞書にデシリアライズし、その辞書を使ってクラスのインスタンスを生成します。

import json
class Person:
    def __init__(self, name, age, city):
        self.name = name
        self.age = age
        self.city = city
json_data = '{"name": "Alice", "age": 30, "city": "Tokyo"}'
data = json.loads(json_data)
person = Person(**data)
print(person.name, person.age, person.city)
# 出力: Alice 30 Tokyo

カスタムデシリアライザの作成

カスタムデシリアライザを作成することで、より複雑なクラスのデシリアライズが可能になります。

これには、デシリアライズ時に特定の処理を行う関数を定義します。

カスタムデシリアライザの実装例

以下は、カスタムデシリアライザを使用してクラスのインスタンスを生成する例です。

import json
class Person:
    def __init__(self, name, age, city):
        self.name = name
        self.age = age
        self.city = city
def custom_person_decoder(dct):
    if 'name' in dct and 'age' in dct and 'city' in dct:
        return Person(dct['name'], dct['age'], dct['city'])
    return dct
json_data = '{"name": "Alice", "age": 30, "city": "Tokyo"}'
person = json.loads(json_data, object_hook=custom_person_decoder)
print(person.name, person.age, person.city)
# 出力: Alice 30 Tokyo

辞書からクラスインスタンスを生成する方法

辞書からクラスインスタンスを生成するためには、辞書のキーとクラスの属性が一致している必要があります。

**演算子を使用して辞書をアンパックし、クラスのコンストラクタに渡します。

data = {'name': 'Alice', 'age': 30, 'city': 'Tokyo'}
person = Person(**data)
print(person.name, person.age, person.city)
# 出力: Alice 30 Tokyo

__init__メソッドの利用

クラスの__init__メソッドを利用して、デシリアライズされたデータをクラスのインスタンスに変換します。

これにより、クラスの属性が正しく初期化されます。

class Person:
    def __init__(self, name, age, city):
        self.name = name
        self.age = age
        self.city = city
data = {'name': 'Alice', 'age': 30, 'city': 'Tokyo'}
person = Person(**data)
print(person.name, person.age, person.city)
# 出力: Alice 30 Tokyo

複雑なクラスのデシリアライズ

複雑なクラスのデシリアライズには、ネストされたオブジェクトやカスタムデシリアライザを使用します。

以下は、ネストされたオブジェクトを含むクラスのデシリアライズ例です。

import json
class Address:
    def __init__(self, street, city):
        self.street = street
        self.city = city
class Person:
    def __init__(self, name, age, address):
        self.name = name
        self.age = age
        self.address = address
def custom_decoder(dct):
    if 'street' in dct and 'city' in dct:
        return Address(dct['street'], dct['city'])
    if 'name' in dct and 'age' in dct and 'address' in dct:
        address = custom_decoder(dct['address'])
        return Person(dct['name'], dct['age'], address)
    return dct
json_data = '''
{
    "name": "Alice",
    "age": 30,
    "address": {
        "street": "123 Main St",
        "city": "Tokyo"
    }
}
'''
person = json.loads(json_data, object_hook=custom_decoder)
print(person.name, person.age, person.address.street, person.address.city)
# 出力: Alice 30 123 Main St Tokyo

ネストされたオブジェクトのデシリアライズ

ネストされたオブジェクトのデシリアライズには、カスタムデシリアライザを再帰的に呼び出す必要があります。

上記の例では、custom_decoder関数がネストされたAddressオブジェクトを正しくデシリアライズしています。

再帰的なデシリアライズ方法

再帰的なデシリアライズ方法を使用することで、複雑なネスト構造を持つJSONデータを正しくデシリアライズできます。

再帰的なデシリアライザは、各レベルのオブジェクトを個別に処理し、最終的に完全なオブジェクトを生成します。

def custom_decoder(dct):
    if 'street' in dct and 'city' in dct:
        return Address(dct['street'], dct['city'])
    if 'name' in dct and 'age' in dct and 'address' in dct:
        address = custom_decoder(dct['address'])
        return Person(dct['name'], dct['age'], address)
    return dct
json_data = '''
{
    "name": "Alice",
    "age": 30,
    "address": {
        "street": "123 Main St",
        "city": "Tokyo"
    }
}
'''
person = json.loads(json_data, object_hook=custom_decoder)
print(person.name, person.age, person.address.street, person.address.city)
# 出力: Alice 30 123 Main St Tokyo

このようにして、PythonでクラスをJSONにシリアライズおよびデシリアライズする方法を理解することができます。

カスタムシリアライザとデシリアライザを使用することで、複雑なオブジェクトも簡単に扱うことができます。

目次から探す