高度なJSON構造とパターン
複雑なJSONスキーマ、設計パターン、ベストプラクティス。スケーラブルで保守可能なJSON構造を設計。
Big JSON Team
• Technical WriterExpert in JSON data manipulation, API development, and web technologies. Passionate about creating tools that make developers' lives easier.
# 高度なJSON構造とパターン
スケーラブルで保守可能な複雑なJSON構造を設計する方法を学びます。
設計原則
1. 一貫性
良い例:{
"users": [
{"id": 1, "name": "田中太郎", "email": "tanaka@example.com"},
{"id": 2, "name": "佐藤花子", "email": "sato@example.com"}
],
"posts": [
{"id": 1, "title": "記事1", "authorId": 1},
{"id": 2, "title": "記事2", "authorId": 2}
]
}
悪い例:
{
"users": [
{"userId": 1, "userName": "田中太郎"},
{"id": 2, "name": "佐藤花子"}
],
"posts": {
"1": {"title": "記事1"},
"2": {"title": "記事2"}
}
}
2. 自己記述的
良い例:{
"product": {
"id": "P123",
"name": "ノートPC",
"price": {
"amount": 150000,
"currency": "JPY"
},
"availability": {
"inStock": true,
"quantity": 50,
"warehouse": "Tokyo-01"
}
}
}
3. 拡張性
バージョニング:{
"version": "1.0",
"data": {
"user": {
"id": 1,
"name": "田中太郎"
}
}
}
一般的なパターン
1. エンベロープパターン
APIレスポンスのラッピング:
{
"status": "success",
"data": {
"users": [
{"id": 1, "name": "田中太郎"}
]
},
"metadata": {
"total": 100,
"page": 1,
"perPage": 10
},
"links": {
"self": "/api/users?page=1",
"next": "/api/users?page=2",
"prev": null
}
}
2. HAL (Hypertext Application Language)
{
"id": 1,
"name": "田中太郎",
"email": "tanaka@example.com",
"_links": {
"self": {
"href": "/users/1"
},
"posts": {
"href": "/users/1/posts"
},
"avatar": {
"href": "/users/1/avatar",
"type": "image/jpeg"
}
},
"_embedded": {
"posts": [
{
"id": 1,
"title": "最初の投稿",
"_links": {
"self": {"href": "/posts/1"}
}
}
]
}
}
3. JSON API仕様
{
"data": {
"type": "users",
"id": "1",
"attributes": {
"name": "田中太郎",
"email": "tanaka@example.com"
},
"relationships": {
"posts": {
"links": {
"related": "/users/1/posts"
},
"data": [
{"type": "posts", "id": "1"},
{"type": "posts", "id": "2"}
]
}
}
},
"included": [
{
"type": "posts",
"id": "1",
"attributes": {
"title": "最初の投稿",
"content": "コンテンツ..."
}
}
]
}
4. イベントソーシング
{
"eventId": "evt_123",
"eventType": "user.created",
"timestamp": "2026-01-16T10:00:00Z",
"version": "1.0",
"aggregateId": "user_1",
"aggregateType": "User",
"data": {
"userId": 1,
"name": "田中太郎",
"email": "tanaka@example.com"
},
"metadata": {
"correlationId": "cor_456",
"causationId": "evt_122",
"userId": "admin_1"
}
}
複雑なリレーションシップ
1. 正規化 vs 非正規化
正規化(データベース風)
{
"users": {
"1": {
"id": 1,
"name": "田中太郎",
"departmentId": 10
},
"2": {
"id": 2,
"name": "佐藤花子",
"departmentId": 10
}
},
"departments": {
"10": {
"id": 10,
"name": "開発部",
"managerId": 1
}
}
}
利点:
- データ重複なし
- 更新が容易
- データサイズが小さい
非正規化(埋め込み)
{
"users": [
{
"id": 1,
"name": "田中太郎",
"department": {
"id": 10,
"name": "開発部"
}
},
{
"id": 2,
"name": "佐藤花子",
"department": {
"id": 10,
"name": "開発部"
}
}
]
}
利点:
- クエリが簡単
- 結合不要
- パフォーマンス向上
2. グラフ構造
{
"nodes": [
{
"id": "user_1",
"type": "user",
"data": {
"name": "田中太郎"
}
},
{
"id": "post_1",
"type": "post",
"data": {
"title": "投稿1"
}
}
],
"edges": [
{
"id": "edge_1",
"source": "user_1",
"target": "post_1",
"type": "authored",
"data": {
"createdAt": "2026-01-16T10:00:00Z"
}
}
]
}
ポリモーフィズム
1. 型識別子パターン
{
"items": [
{
"type": "text",
"id": 1,
"content": "これはテキストです"
},
{
"type": "image",
"id": 2,
"url": "https://example.com/image.jpg",
"alt": "画像の説明"
},
{
"type": "video",
"id": 3,
"url": "https://example.com/video.mp4",
"duration": 120,
"thumbnail": "https://example.com/thumb.jpg"
}
]
}
2. TypeScript型定義
type TextItem = {
type: 'text';
id: number;
content: string;
};
type ImageItem = {
type: 'image';
id: number;
url: string;
alt: string;
};
type VideoItem = {
type: 'video';
id: number;
url: string;
duration: number;
thumbnail: string;
};
type Item = TextItem | ImageItem | VideoItem;
interface Document {
items: Item[];
}
3. 処理
function renderItem(item) {
switch (item.type) {
case 'text':
return <p>${item.content}</p>;
case 'image':
return <img src="${item.url}" alt="${item.alt}" />;
case 'video':
return <video src="${item.url}" poster="${item.thumbnail}"></video>;
default:
throw new Error(Unknown item type: ${item.type});
}
}
時系列データ
1. タイムスタンプ付きイベント
{
"sensor": "temp_sensor_01",
"location": "Tokyo-DC1-Rack5",
"measurements": [
{
"timestamp": "2026-01-16T10:00:00Z",
"value": 22.5,
"unit": "celsius"
},
{
"timestamp": "2026-01-16T10:01:00Z",
"value": 22.7,
"unit": "celsius"
},
{
"timestamp": "2026-01-16T10:02:00Z",
"value": 22.6,
"unit": "celsius"
}
],
"metadata": {
"samplingInterval": 60,
"accuracy": 0.1
}
}
2. 集約データ
{
"sensor": "temp_sensor_01",
"period": {
"start": "2026-01-16T10:00:00Z",
"end": "2026-01-16T11:00:00Z"
},
"statistics": {
"min": 22.3,
"max": 23.1,
"avg": 22.7,
"median": 22.6,
"stdDev": 0.2
},
"samples": 60
}
ページネーション
1. オフセットベース
{
"data": [
{"id": 11, "name": "Item 11"},
{"id": 12, "name": "Item 12"}
],
"pagination": {
"total": 100,
"offset": 10,
"limit": 10,
"hasMore": true
},
"links": {
"first": "/api/items?offset=0&limit=10",
"prev": "/api/items?offset=0&limit=10",
"next": "/api/items?offset=20&limit=10",
"last": "/api/items?offset=90&limit=10"
}
}
2. カーソルベース
{
"data": [
{
"id": "item_11",
"name": "Item 11",
"createdAt": "2026-01-16T10:11:00Z"
}
],
"cursors": {
"before": "Y3Vyc29yX2JlZm9yZQ==",
"after": "Y3Vyc29yX2FmdGVy"
},
"pageInfo": {
"hasNextPage": true,
"hasPreviousPage": true
}
}
エラーハンドリング
1. 構造化エラー
{
"error": {
"code": "VALIDATION_ERROR",
"message": "入力データが無効です",
"details": [
{
"field": "email",
"message": "有効なメールアドレスを入力してください",
"code": "INVALID_EMAIL"
},
{
"field": "age",
"message": "年齢は0以上である必要があります",
"code": "MIN_VALUE",
"min": 0
}
],
"timestamp": "2026-01-16T10:00:00Z",
"requestId": "req_123"
}
}
2. Problem Details (RFC 7807)
{
"type": "https://example.com/probs/validation-error",
"title": "Validation Error",
"status": 400,
"detail": "入力データが無効です",
"instance": "/api/users",
"errors": [
{
"field": "email",
"message": "有効なメールアドレスを入力してください"
}
]
}
バージョニング戦略
1. URLバージョニング
// v1
{
"name": "田中太郎"
}
// v2
{
"firstName": "太郎",
"lastName": "田中"
}
APIエンドポイント:
/api/v1/users/api/v2/users
2. スキーマバージョニング
{
"schemaVersion": "2.0",
"data": {
"firstName": "太郎",
"lastName": "田中"
},
"deprecated": {
"name": "田中太郎"
}
}
3. 後方互換性
{
"name": "田中太郎",
"firstName": "太郎",
"lastName": "田中",
"_meta": {
"deprecations": [
{
"field": "name",
"replacedBy": ["firstName", "lastName"],
"removedInVersion": "3.0"
}
]
}
}
検証とスキーマ
JSON Schema
{
"$schema": "http://json-schema.org/draft-07/schema#",
"title": "User",
"type": "object",
"properties": {
"id": {
"type": "integer",
"minimum": 1
},
"name": {
"type": "string",
"minLength": 1,
"maxLength": 100
},
"email": {
"type": "string",
"format": "email"
},
"age": {
"type": "integer",
"minimum": 0,
"maximum": 150
},
"roles": {
"type": "array",
"items": {
"type": "string",
"enum": ["admin", "user", "guest"]
},
"minItems": 1,
"uniqueItems": true
},
"metadata": {
"type": "object",
"additionalProperties": {
"type": "string"
}
}
},
"required": ["id", "name", "email"],
"additionalProperties": false
}
パフォーマンス最適化
1. フィールド選択
リクエスト:
GET /api/users/1?fields=id,name,email
レスポンス:
{
"id": 1,
"name": "田中太郎",
"email": "tanaka@example.com"
}
2. スパースフィールドセット(JSON API)
GET /api/users?fields[users]=name,email&fields[posts]=title
3. バッチリクエスト
{
"requests": [
{
"id": "req1",
"method": "GET",
"path": "/users/1"
},
{
"id": "req2",
"method": "GET",
"path": "/posts/1"
}
]
}
レスポンス:
{
"responses": [
{
"id": "req1",
"status": 200,
"body": {"id": 1, "name": "田中太郎"}
},
{
"id": "req2",
"status": 200,
"body": {"id": 1, "title": "投稿1"}
}
]
}
セキュリティ考慮事項
1. センシティブデータのフィルタリング
function sanitizeUser(user) {
const { password, ssn, creditCard, ...safe } = user;
return safe;
}
const publicUser = sanitizeUser(user);
2. レート制限情報
{
"data": {...},
"rateLimit": {
"limit": 1000,
"remaining": 995,
"reset": 1516239022
}
}
ベストプラクティス
1. 命名規則
{
"userId": 1, // camelCase(JavaScript)
"user_id": 1, // snake_case(Python/Ruby)
"createdAt": "...", // 一貫性を保つ
"isActive": true // boolean はis/has接頭辞
}
2. null vs 省略
// オプションフィールド
{
"name": "太郎",
"email": null // 明示的にnull
}
// または省略
{
"name": "太郎"
}
3. 日付形式
{
"createdAt": "2026-01-16T10:00:00Z", // ISO 8601
"updatedAt": "2026-01-16T10:00:00.123Z",
"date": "2026-01-16"
}
まとめ
設計チェックリスト
- [ ] 一貫した命名規則
- [ ] 適切なネスト深度(3-4レベル以下)
- [ ] バージョニング戦略
- [ ] エラーハンドリング
- [ ] ページネーション(必要な場合)
- [ ] スキーマ検証
- [ ] ドキュメント
- [ ] セキュリティ考慮
- [ ] パフォーマンス最適化
推奨パターン
- API → JSON API / HAL
- イベント → Event Sourcing Pattern
- 設定 → バージョニング + 検証
- 時系列 → タイムスタンプ配列
スケーラブルで保守可能なJSON構造を設計しましょう!