← ブログに戻る

高度なJSON構造とパターン

複雑なJSONスキーマ、設計パターン、ベストプラクティス。スケーラブルで保守可能なJSON構造を設計。

Big JSON Team16 分で読めますadvanced
B

Big JSON Team

Technical Writer

Expert in JSON data manipulation, API development, and web technologies. Passionate about creating tools that make developers' lives easier.

16 分読む

# 高度な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構造を設計しましょう!

Share:

関連記事

Read in English