marshmallow

バリデーションライブラリPythonシリアライゼーションスキーマオブジェクト変換軽量

ライブラリ

marshmallow

概要

marshmallowは、複雑なオブジェクトとシンプルなPythonデータ型の相互変換を行う軽量ライブラリです。データのバリデーション、シリアライゼーション、デシリアライゼーションの3つの主要機能を提供し、Python Webアプリケーション開発の標準的なツールとして広く採用されています。2025年現在、バージョン4.0.0が最新で、Flask、FastAPI、SQLAlchemy等との統合により、エンタープライズレベルのPythonアプリケーションで重要な地位を占めています。Tidelift Subscriptionによる商用サポートも提供されています。

詳細

marshmallow 4.0系は2025年現在の最新メジャーバージョンで、スキーマベースのデータ処理アプローチを採用。入力データの検証、アプリケーションレベルオブジェクトへのデシリアライゼーション、プリミティブPython型へのシリアライゼーションという3段階処理により、HTTP APIでのJSON処理など、標準フォーマットでのデータレンダリングを効率化します。Pythonオブジェクト指向パラダイムでの強力な抽象化ソリューションを提供し、他言語のバリデーションライブラリと類似したスキーマ定義手法により、多くの開発者に支持されています。

主な特徴

  • 3段階データ処理: バリデーション、デシリアライゼーション、シリアライゼーション
  • スキーマベース設計: 宣言的で再利用可能なスキーマ定義
  • フレームワーク統合: Flask、FastAPI、SQLAlchemy等との標準的統合
  • 軽量アーキテクチャ: 最小限の依存関係で高いパフォーマンス
  • 豊富なフィールド型: 内蔵フィールドと拡張可能なカスタムフィールド
  • 商用サポート: Tideliftによるエンタープライズサポート

メリット・デメリット

メリット

  • Pythonエコシステムでの標準的地位と成熟度
  • Flask、FastAPI等主要フレームワークとの優れた統合
  • スキーマ定義による直感的で保守性の高いAPI
  • 軽量でありながら豊富な機能セット
  • ネストしたオブジェクト構造の自然な処理
  • 部分的シリアライゼーション(only/exclude)の柔軟性

デメリット

  • Pydanticと比較して型ヒントサポートが限定的
  • スキーマクラス定義の冗長性(ボイラープレート)
  • ランタイムでのパフォーマンスオーバーヘッド
  • 複雑なバリデーションロジックでの設定の複雑化
  • モダンPythonの型システムとの統合に課題
  • エラーハンドリングの学習コストがやや高い

参考ページ

書き方の例

インストールと基本セットアップ

# marshmallowのインストール
pip install marshmallow
poetry add marshmallow
pipenv install marshmallow

# エクストラ機能(オプション)
pip install marshmallow[dev]  # 開発用依存関係
pip install marshmallow[reco] # 推奨依存関係

# Python 3.8以上が必要
# 軽量で依存関係最小限

基本的なスキーマ定義とシリアライゼーション

from marshmallow import Schema, fields
from dataclasses import dataclass
from datetime import datetime
from pprint import pprint

# データクラスの定義
@dataclass
class User:
    name: str
    email: str
    created_at: datetime

# marshmallowスキーマの定義
class UserSchema(Schema):
    name = fields.Str()
    email = fields.Email()
    created_at = fields.DateTime()

# ユーザーオブジェクトの作成
user = User(
    name="田中太郎",
    email="[email protected]",
    created_at=datetime.now()
)

# シリアライゼーション(オブジェクト → 辞書)
schema = UserSchema()
result = schema.dump(user)
pprint(result)
# {'name': '田中太郎',
#  'email': '[email protected]', 
#  'created_at': '2025-06-22T10:30:45.123456'}

# 複数オブジェクトのシリアライゼーション
users = [
    User("山田花子", "[email protected]", datetime.now()),
    User("佐藤次郎", "[email protected]", datetime.now())
]
schema_many = UserSchema(many=True)
results = schema_many.dump(users)
print(f"ユーザー数: {len(results)}")

# 部分的シリアライゼーション(フィールド選択)
summary_schema = UserSchema(only=("name", "email"))
summary = summary_schema.dump(user)
pprint(summary)
# {'name': '田中太郎', 'email': '[email protected]'}

# フィールド除外
no_timestamp_schema = UserSchema(exclude=("created_at",))
no_timestamp = no_timestamp_schema.dump(user)
pprint(no_timestamp)
# {'name': '田中太郎', 'email': '[email protected]'}

デシリアライゼーションとバリデーション

from marshmallow import Schema, fields, ValidationError
from pprint import pprint

class UserSchema(Schema):
    name = fields.Str(required=True)
    email = fields.Email(required=True)
    age = fields.Int(validate=lambda x: 0 <= x <= 150)
    created_at = fields.DateTime()

# 有効なデータのデシリアライゼーション
user_data = {
    "name": "田中太郎",
    "email": "[email protected]",
    "age": 30,
    "created_at": "2025-06-22T10:30:45.123456"
}

schema = UserSchema()
try:
    result = schema.load(user_data)
    pprint(result)
    # {'name': '田中太郎',
    #  'email': '[email protected]',
    #  'age': 30,
    #  'created_at': datetime.datetime(2025, 6, 22, 10, 30, 45, 123456)}
    print("デシリアライゼーション成功")
except ValidationError as err:
    print("バリデーションエラー:", err.messages)

# 無効なデータでのバリデーションエラー
invalid_data = {
    "name": "",  # 空文字
    "email": "invalid-email",  # 無効なメール形式
    "age": 200,  # 範囲外の値
    "created_at": "invalid-date"  # 無効な日付形式
}

try:
    result = schema.load(invalid_data)
except ValidationError as err:
    print("バリデーションエラー:")
    for field, errors in err.messages.items():
        print(f"  {field}: {errors}")
    
    # 有効だったデータも取得可能
    if err.valid_data:
        print("有効だったデータ:", err.valid_data)

# バリデーションのみ(エラーチェック)
validation_result = schema.validate(invalid_data)
if validation_result:
    print("バリデーションエラー発見:", validation_result)
else:
    print("バリデーション成功")

# 部分的デシリアライゼーション
partial_data = {"name": "田中太郎"}
try:
    result = schema.load(partial_data, partial=True)
    print("部分的デシリアライゼーション成功:", result)
except ValidationError as err:
    print("エラー:", err.messages)

ネストしたスキーマと複雑な構造

from marshmallow import Schema, fields
from dataclasses import dataclass
from typing import List, Optional
from datetime import datetime

# 複雑なデータ構造の定義
@dataclass
class Address:
    street: str
    city: str
    postal_code: str
    country: str

@dataclass
class Company:
    name: str
    industry: str

@dataclass
class User:
    name: str
    email: str
    address: Address
    company: Optional[Company]
    skills: List[str]
    created_at: datetime

# ネストしたスキーマの定義
class AddressSchema(Schema):
    street = fields.Str(required=True)
    city = fields.Str(required=True)
    postal_code = fields.Str(required=True)
    country = fields.Str(required=True)

class CompanySchema(Schema):
    name = fields.Str(required=True)
    industry = fields.Str(required=True)

class UserSchema(Schema):
    name = fields.Str(required=True)
    email = fields.Email(required=True)
    address = fields.Nested(AddressSchema, required=True)
    company = fields.Nested(CompanySchema, allow_none=True)
    skills = fields.List(fields.Str(), required=True)
    created_at = fields.DateTime()

# 複雑なデータの作成
complex_data = {
    "name": "田中太郎",
    "email": "[email protected]",
    "address": {
        "street": "渋谷区道玄坂1-2-3",
        "city": "東京",
        "postal_code": "150-0043",
        "country": "Japan"
    },
    "company": {
        "name": "株式会社サンプル",
        "industry": "IT"
    },
    "skills": ["Python", "JavaScript", "Go"],
    "created_at": "2025-06-22T10:30:45.123456"
}

schema = UserSchema()

# 複雑なデータのデシリアライゼーション
try:
    result = schema.load(complex_data)
    print("複雑なデータのデシリアライゼーション成功")
    print(f"住所: {result['address']['city']}")
    print(f"スキル数: {len(result['skills'])}")
except ValidationError as err:
    print("エラー:", err.messages)

# ネストしたフィールドの部分的シリアライゼーション
address_only_schema = UserSchema(only=("name", "address.city", "address.country"))
user_obj = User(
    name="田中太郎",
    email="[email protected]",
    address=Address("渋谷区道玄坂1-2-3", "東京", "150-0043", "Japan"),
    company=Company("株式会社サンプル", "IT"),
    skills=["Python", "JavaScript"],
    created_at=datetime.now()
)

# 特定のネストフィールドのみ取得
address_result = UserSchema(only=("name", "address")).dump(user_obj)
print("住所のみ:", address_result)

# リストでのネストオブジェクト
teams_data = {
    "name": "開発チーム",
    "members": [
        {"name": "田中太郎", "email": "[email protected]"},
        {"name": "山田花子", "email": "[email protected]"}
    ]
}

class TeamSchema(Schema):
    name = fields.Str(required=True)
    members = fields.List(fields.Nested(UserSchema(only=("name", "email"))))

team_schema = TeamSchema()
team_result = team_schema.load(teams_data)
print(f"チームメンバー数: {len(team_result['members'])}")

カスタムバリデーション関数と高度な機能

from marshmallow import Schema, fields, validates, validates_schema, ValidationError, post_load
import re
from datetime import datetime, date

class AdvancedUserSchema(Schema):
    username = fields.Str(required=True)
    password = fields.Str(required=True, load_only=True)  # シリアライゼーション時は除外
    confirm_password = fields.Str(required=True, load_only=True)
    email = fields.Email(required=True)
    phone = fields.Str(required=True)
    birth_date = fields.Date(required=True)
    created_at = fields.DateTime(dump_only=True, default=datetime.now)  # デシリアライゼーション時は無視

    @validates('username')
    def validate_username(self, value):
        """ユーザー名のカスタムバリデーション"""
        if len(value) < 3:
            raise ValidationError('ユーザー名は3文字以上である必要があります')
        if not re.match(r'^[a-zA-Z0-9_]+$', value):
            raise ValidationError('ユーザー名は英数字とアンダースコアのみ使用可能です')
        
        # 予約語チェック
        reserved_words = ['admin', 'root', 'system', 'test']
        if value.lower() in reserved_words:
            raise ValidationError('このユーザー名は予約されています')

    @validates('password')
    def validate_password(self, value):
        """パスワード強度チェック"""
        if len(value) < 8:
            raise ValidationError('パスワードは8文字以上である必要があります')
        
        # パスワード強度チェック
        if not re.search(r'[A-Z]', value):
            raise ValidationError('パスワードには大文字を含める必要があります')
        if not re.search(r'[a-z]', value):
            raise ValidationError('パスワードには小文字を含める必要があります')
        if not re.search(r'\d', value):
            raise ValidationError('パスワードには数字を含める必要があります')
        if not re.search(r'[!@#$%^&*(),.?":{}|<>]', value):
            raise ValidationError('パスワードには特殊文字を含める必要があります')

    @validates('phone')
    def validate_phone(self, value):
        """日本の電話番号形式チェック"""
        # 日本の電話番号パターン
        patterns = [
            r'^0\d{1,4}-\d{1,4}-\d{4}$',  # 固定電話
            r'^(070|080|090)-\d{4}-\d{4}$',  # 携帯電話
        ]
        
        if not any(re.match(pattern, value) for pattern in patterns):
            raise ValidationError('有効な日本の電話番号形式で入力してください')

    @validates('birth_date')
    def validate_birth_date(self, value):
        """生年月日の妥当性チェック"""
        today = date.today()
        age = today.year - value.year - ((today.month, today.day) < (value.month, value.day))
        
        if age < 13:
            raise ValidationError('13歳以上である必要があります')
        if age > 120:
            raise ValidationError('有効な生年月日を入力してください')

    @validates_schema
    def validate_passwords_match(self, data, **kwargs):
        """スキーマレベルのバリデーション:パスワード一致確認"""
        if 'password' in data and 'confirm_password' in data:
            if data['password'] != data['confirm_password']:
                raise ValidationError({'confirm_password': ['パスワードが一致しません']})

    @post_load
    def make_user(self, data, **kwargs):
        """デシリアライゼーション後の処理"""
        # confirm_passwordは内部処理用なので削除
        data.pop('confirm_password', None)
        # 作成日時を追加
        data['created_at'] = datetime.now()
        return data

# カスタムバリデーション関数
def validate_email_domain(email):
    """特定ドメインのメールアドレスのみ許可"""
    allowed_domains = ['example.com', 'company.com', 'gmail.com']
    domain = email.split('@')[1]
    if domain not in allowed_domains:
        raise ValidationError(f'許可されていないドメインです。許可ドメイン: {", ".join(allowed_domains)}')

class RestrictedUserSchema(AdvancedUserSchema):
    email = fields.Email(required=True, validate=validate_email_domain)

# 高度なバリデーションのテスト
advanced_data = {
    "username": "tanaka_user",
    "password": "SecurePass123!",
    "confirm_password": "SecurePass123!",
    "email": "[email protected]",
    "phone": "090-1234-5678",
    "birth_date": "1990-05-15"
}

schema = AdvancedUserSchema()

try:
    result = schema.load(advanced_data)
    print("高度なバリデーション成功:")
    print(f"ユーザー名: {result['username']}")
    print(f"作成日時: {result['created_at']}")
    # パスワードは含まれない(load_only)
    print("パスワード含まれていない:", 'password' not in result)
except ValidationError as err:
    print("バリデーションエラー:", err.messages)

# 無効なデータでのテスト
invalid_advanced_data = {
    "username": "ad",  # 短すぎる
    "password": "weak",  # 弱いパスワード
    "confirm_password": "different",  # 異なるパスワード
    "email": "[email protected]",  # 許可されていないドメイン
    "phone": "123-456-789",  # 無効な電話番号形式
    "birth_date": "2020-01-01"  # 年齢制限に引っかかる
}

try:
    result = RestrictedUserSchema().load(invalid_advanced_data)
except ValidationError as err:
    print("\n複数エラーのキャッチ:")
    for field, errors in err.messages.items():
        print(f"  {field}: {errors}")

WebフレームワークとAPI統合

from marshmallow import Schema, fields, ValidationError
from flask import Flask, request, jsonify
import json

# Flask統合例
app = Flask(__name__)

class CreateUserSchema(Schema):
    name = fields.Str(required=True)
    email = fields.Email(required=True)
    age = fields.Int(required=True, validate=lambda x: 18 <= x <= 100)

class UpdateUserSchema(Schema):
    name = fields.Str()
    email = fields.Email()
    age = fields.Int(validate=lambda x: 18 <= x <= 100)

class UserResponseSchema(Schema):
    id = fields.Int()
    name = fields.Str()
    email = fields.Email()
    age = fields.Int()
    created_at = fields.DateTime()

# バリデーション用デコレーター
def validate_json(schema_class):
    def decorator(f):
        def wrapper(*args, **kwargs):
            try:
                schema = schema_class()
                validated_data = schema.load(request.json)
                return f(validated_data, *args, **kwargs)
            except ValidationError as err:
                return jsonify({
                    'error': 'バリデーションエラー',
                    'messages': err.messages
                }), 400
        wrapper.__name__ = f.__name__
        return wrapper
    return decorator

@app.route('/users', methods=['POST'])
@validate_json(CreateUserSchema)
def create_user(validated_data):
    """ユーザー作成API"""
    # データベース保存処理(省略)
    user_data = {
        'id': 1,
        'created_at': '2025-06-22T10:30:45.123456',
        **validated_data
    }
    
    # レスポンスのシリアライゼーション
    response_schema = UserResponseSchema()
    return jsonify(response_schema.dump(user_data)), 201

@app.route('/users/<int:user_id>', methods=['PUT'])
@validate_json(UpdateUserSchema)
def update_user(validated_data, user_id):
    """ユーザー更新API"""
    # データベース更新処理(省略)
    user_data = {
        'id': user_id,
        'name': validated_data.get('name', '既存の名前'),
        'email': validated_data.get('email', '[email protected]'),
        'age': validated_data.get('age', 25),
        'created_at': '2025-06-22T10:30:45.123456'
    }
    
    response_schema = UserResponseSchema()
    return jsonify(response_schema.dump(user_data))

# FastAPI統合例(概念実装)
from typing import Optional

class FastAPIIntegration:
    """FastAPIでのmarshmallow統合パターン"""
    
    @staticmethod
    def validate_request_body(schema_class, data: dict):
        """リクエストボディのバリデーション"""
        try:
            schema = schema_class()
            return schema.load(data), None
        except ValidationError as err:
            return None, err.messages
    
    @staticmethod
    def serialize_response(schema_class, data):
        """レスポンスのシリアライゼーション"""
        schema = schema_class()
        return schema.dump(data)

# 使用例
def fastapi_create_user_handler(request_data: dict):
    """FastAPI風のハンドラー例"""
    validated_data, errors = FastAPIIntegration.validate_request_body(
        CreateUserSchema, request_data
    )
    
    if errors:
        return {"error": "Validation failed", "details": errors}, 400
    
    # ビジネスロジック処理
    user_data = {
        'id': 1,
        'created_at': '2025-06-22T10:30:45.123456',
        **validated_data
    }
    
    response_data = FastAPIIntegration.serialize_response(
        UserResponseSchema, user_data
    )
    
    return response_data, 200

# SQLAlchemy統合例
class SQLAlchemyIntegration:
    """SQLAlchemyモデルとの統合パターン"""
    
    def __init__(self, model_class, schema_class):
        self.model_class = model_class
        self.schema_class = schema_class
    
    def create_from_dict(self, data: dict):
        """辞書からモデルインスタンス作成"""
        schema = self.schema_class()
        validated_data = schema.load(data)
        return self.model_class(**validated_data)
    
    def serialize_model(self, model_instance):
        """モデルインスタンスをシリアライゼーション"""
        schema = self.schema_class()
        return schema.dump(model_instance)
    
    def bulk_serialize(self, model_instances):
        """複数インスタンスの一括シリアライゼーション"""
        schema = self.schema_class(many=True)
        return schema.dump(model_instances)

# ユースケース例
def api_workflow_example():
    """API処理ワークフローの例"""
    # 1. リクエストデータ
    request_data = {
        "name": "田中太郎",
        "email": "[email protected]",
        "age": 30
    }
    
    # 2. バリデーションとデシリアライゼーション
    create_schema = CreateUserSchema()
    try:
        validated_data = create_schema.load(request_data)
        print("バリデーション成功:", validated_data)
    except ValidationError as err:
        print("バリデーションエラー:", err.messages)
        return
    
    # 3. ビジネスロジック処理(ユーザー作成)
    user_data = {
        'id': 123,
        'created_at': '2025-06-22T10:30:45.123456',
        **validated_data
    }
    
    # 4. レスポンスのシリアライゼーション
    response_schema = UserResponseSchema()
    response_data = response_schema.dump(user_data)
    print("レスポンスデータ:", response_data)

if __name__ == "__main__":
    api_workflow_example()

高度なスキーマ設計とベストプラクティス

from marshmallow import Schema, fields, validates_schema, ValidationError, pre_load, post_load, pre_dump, post_dump
from marshmallow.validate import Length, Range, OneOf
from typing import Dict, Any
import json

class BaseTimestampSchema(Schema):
    """タイムスタンプを含む基底スキーマ"""
    created_at = fields.DateTime(dump_only=True)
    updated_at = fields.DateTime(dump_only=True)

class PaginationSchema(Schema):
    """ページネーション用スキーマ"""
    page = fields.Int(required=True, validate=Range(min=1))
    per_page = fields.Int(required=True, validate=Range(min=1, max=100))
    total = fields.Int(dump_only=True)
    pages = fields.Int(dump_only=True)

class SearchQuerySchema(Schema):
    """検索クエリ用スキーマ"""
    q = fields.Str(required=True, validate=Length(min=1, max=100))
    category = fields.Str(validate=OneOf(['tech', 'business', 'hobby']))
    sort_by = fields.Str(validate=OneOf(['created_at', 'updated_at', 'name']))
    sort_order = fields.Str(validate=OneOf(['asc', 'desc']), default='desc')

class DynamicFieldsSchema(Schema):
    """動的フィールド選択をサポートするスキーマ"""
    
    def __init__(self, only_fields=None, exclude_fields=None, *args, **kwargs):
        super().__init__(*args, **kwargs)
        if only_fields:
            self.only = only_fields
        if exclude_fields:
            self.exclude = exclude_fields

class UserProfileSchema(BaseTimestampSchema, DynamicFieldsSchema):
    """ユーザープロファイルスキーマ"""
    id = fields.Int(dump_only=True)
    username = fields.Str(required=True)
    email = fields.Email(required=True)
    full_name = fields.Str(required=True)
    bio = fields.Str(allow_none=True)
    avatar_url = fields.Url(allow_none=True)
    is_active = fields.Bool(default=True)
    
    # プライベート情報(条件付きで除外)
    phone = fields.Str(allow_none=True)
    birth_date = fields.Date(allow_none=True)

    @pre_load
    def preprocess_input(self, data, **kwargs):
        """入力データの前処理"""
        # 文字列の正規化
        if 'full_name' in data:
            data['full_name'] = data['full_name'].strip()
        if 'username' in data:
            data['username'] = data['username'].lower().strip()
        return data

    @post_load
    def validate_and_transform(self, data, **kwargs):
        """ロード後の検証と変換"""
        # ユーザー名の重複チェック(実際のアプリではDB確認)
        existing_usernames = ['admin', 'root', 'test']
        if data.get('username') in existing_usernames:
            raise ValidationError({'username': ['このユーザー名は使用できません']})
        
        return data

    @pre_dump
    def prepare_for_serialization(self, data, **kwargs):
        """シリアライゼーション前の準備"""
        # コンテキストに基づいてプライベート情報を除外
        context = self.context or {}
        is_owner = context.get('is_owner', False)
        is_admin = context.get('is_admin', False)
        
        if not (is_owner or is_admin):
            # 他人のプロファイルの場合、プライベート情報を除外
            sensitive_fields = ['phone', 'birth_date']
            for field in sensitive_fields:
                if hasattr(data, field):
                    setattr(data, field, None)
                elif isinstance(data, dict) and field in data:
                    data.pop(field, None)
        
        return data

    @post_dump
    def add_computed_fields(self, data, **kwargs):
        """計算フィールドの追加"""
        # 年齢の計算(生年月日から)
        if data.get('birth_date'):
            from datetime import date
            birth_date = data['birth_date']
            if isinstance(birth_date, str):
                birth_date = date.fromisoformat(birth_date)
            today = date.today()
            age = today.year - birth_date.year - ((today.month, today.day) < (birth_date.month, birth_date.day))
            data['age'] = age
        
        return data

class BlogPostSchema(BaseTimestampSchema):
    """ブログ投稿スキーマ"""
    id = fields.Int(dump_only=True)
    title = fields.Str(required=True, validate=Length(min=1, max=200))
    content = fields.Str(required=True, validate=Length(min=10))
    slug = fields.Str(dump_only=True)
    status = fields.Str(validate=OneOf(['draft', 'published', 'archived']), default='draft')
    author = fields.Nested(UserProfileSchema(only_fields=['id', 'username', 'full_name']))
    tags = fields.List(fields.Str(), default=list)
    view_count = fields.Int(dump_only=True, default=0)

    @validates_schema
    def validate_content_and_title(self, data, **kwargs):
        """タイトルとコンテンツの関連バリデーション"""
        title = data.get('title', '')
        content = data.get('content', '')
        
        # タイトルが短すぎる場合のコンテンツ長制限
        if len(title) < 10 and len(content) < 100:
            raise ValidationError('短いタイトルの場合、コンテンツは100文字以上である必要があります')

class ApiResponseSchema(Schema):
    """API レスポンス用の統一スキーマ"""
    success = fields.Bool(default=True)
    message = fields.Str()
    data = fields.Raw()
    pagination = fields.Nested(PaginationSchema, allow_none=True)
    
    @post_dump
    def remove_none_values(self, data, **kwargs):
        """None値を削除"""
        return {k: v for k, v in data.items() if v is not None}

class SchemaManager:
    """スキーマ管理とユーティリティクラス"""
    
    @staticmethod
    def create_api_response(data=None, message="成功", success=True, pagination=None):
        """API レスポンスの作成"""
        response_data = {
            'success': success,
            'message': message,
            'data': data,
            'pagination': pagination
        }
        return ApiResponseSchema().dump(response_data)
    
    @staticmethod
    def validate_and_serialize_list(schema_class, data_list, context=None):
        """リストデータのバリデーションとシリアライゼーション"""
        try:
            schema = schema_class(many=True, context=context)
            validated_data = schema.load(data_list)
            serialized_data = schema.dump(validated_data)
            return serialized_data, None
        except ValidationError as err:
            return None, err.messages
    
    @staticmethod
    def dynamic_schema_serialization(schema_class, data, only_fields=None, exclude_fields=None, context=None):
        """動的フィールド選択でのシリアライゼーション"""
        schema = schema_class(
            only_fields=only_fields,
            exclude_fields=exclude_fields,
            context=context
        )
        return schema.dump(data)

# 実践的な使用例
def advanced_schema_example():
    """高度なスキーマ使用例"""
    
    # ユーザーデータ
    user_data = {
        'username': '  TanakaUser  ',  # 前処理でトリムされる
        'email': '[email protected]',
        'full_name': '田中太郎',
        'bio': 'Python開発者です',
        'phone': '090-1234-5678',
        'birth_date': '1990-05-15'
    }
    
    # 1. 所有者としてのシリアライゼーション(プライベート情報含む)
    user_schema_owner = UserProfileSchema(context={'is_owner': True})
    try:
        validated_user = user_schema_owner.load(user_data)
        serialized_owner = user_schema_owner.dump(validated_user)
        print("所有者表示:", json.dumps(serialized_owner, indent=2, ensure_ascii=False))
    except ValidationError as err:
        print("バリデーションエラー:", err.messages)
    
    # 2. 他人としてのシリアライゼーション(プライベート情報除外)
    user_schema_public = UserProfileSchema(context={'is_owner': False})
    serialized_public = user_schema_public.dump(validated_user)
    print("\n公開表示:", json.dumps(serialized_public, indent=2, ensure_ascii=False))
    
    # 3. API レスポンスの作成
    response = SchemaManager.create_api_response(
        data=serialized_public,
        message="ユーザー情報を取得しました"
    )
    print("\nAPI レスポンス:", json.dumps(response, indent=2, ensure_ascii=False))
    
    # 4. 動的フィールド選択
    summary_data = SchemaManager.dynamic_schema_serialization(
        UserProfileSchema,
        validated_user,
        only_fields=['username', 'full_name', 'bio'],
        context={'is_owner': False}
    )
    print("\n要約表示:", json.dumps(summary_data, indent=2, ensure_ascii=False))

if __name__ == "__main__":
    advanced_schema_example()