---
title: JSON Schemaパターンサンプル
description: Kurocoのコンテンツ定義では、JSON型の拡張項目を使って柔軟なフォームUIを構築できます。このページでは、実際に使えるJSON Schemaのパターンを紹介します。
---

## JSON拡張項目の設定方法

Kurocoのコンテンツ定義では、JSON型の拡張項目を使って柔軟なフォームUIを構築できます。
このページでは、実際に使えるJSON Schemaのパターンを紹介します。

:::info
Kurocoで使用するJSONスキーマの構文は、https://json-schema.org/understanding-json-schema/basics がベースです。
基本的な構文については`JSON Schema`のドキュメントを参照してください。
:::

## 基本的なフィールドタイプ

### セレクトボックス(文字列の選択)

```json
{
  "type": "object",
  "properties": {
    "status": {
      "type": "string",
      "title": "ステータス",
      "description": "コンテンツの公開状態を選択してください",
      "enum": ["draft", "published", "archived"],
      "default": "draft"
    }
  }
}
```

**表示**: セレクトボックス
![Image from Gyazo](https://t.gyazo.com/teams/diverta/97c61c1713e36382ea546acdfe6a4c8e.png)

**注意**: `default`が設定されている場合はその値が初期選択されます。

---

### セレクトボックス(表示ラベルのカスタマイズ)

```json
{
  "type": "object",
  "properties": {
    "priority": {
      "type": "string",
      "title": "優先度",
      "enum": ["low", "medium", "high", "urgent"],
      "enumNames": ["低", "中", "高", "緊急"],
      "default": "medium"
    }
  }
}
```

**表示**: セレクトボックス(日本語ラベル)
![Image from Gyazo](https://t.gyazo.com/teams/diverta/5a27ef67fa57fc8f5bc9f5b3144eb083.png)

**注意**: `enumNames`を使用することで、内部的な値(`low`など)とは異なる表示ラベル(`低`など)を設定できます。

---

### セレクトボックス(数値の選択)

```json
{
  "type": "object",
  "properties": {
    "rating": {
      "type": "integer",
      "title": "評価",
      "enum": [1, 2, 3, 4, 5],
      "enumNames": ["★", "★★", "★★★", "★★★★", "★★★★★"]
    }
  }
}
```

**表示**: セレクトボックス(数値)
![Image from Gyazo](https://t.gyazo.com/teams/diverta/20c91a1bc9f2b32c70cc4ec216e5dc09.png)

**注意**: Number/Integer型のenumの場合、`default`未設定時は最初の値が自動選択されます。

---

### 複数選択セレクトボックス

```json
{
  "type": "object",
  "properties": {
    "tags": {
      "type": "array",
      "title": "タグ",
      "description": "複数選択可能です(Ctrl/Cmd + クリック)",
      "items": {
        "type": "string",
        "enum": ["技術", "ビジネス", "デザイン", "マーケティング", "その他"]
      }
    }
  }
}
```

**表示**: 複数選択セレクトボックス
![Image from Gyazo](https://t.gyazo.com/teams/diverta/68288d57fbf109423a6ca28f04a08354.png)

---

## 実用的な入力フォーム

### ファイルマネージャー(画像・ファイル選択)

```json
{
  "type": "object",
  "title": "メディア設定",
  "properties": {
    "thumbnailImage": {
      "type": "string",
      "title": "サムネイル画像",
      "description": "一覧表示用のサムネイル画像を選択してください",
      "format": "file-manager"
    },
    "headerImage": {
      "type": "string",
      "title": "ヘッダー画像",
      "description": "ページ上部に表示する画像",
      "format": "file-manager"
    },
    "pdfDocument": {
      "type": "string",
      "title": "PDFドキュメント",
      "description": "ダウンロード用PDFファイル",
      "format": "file-manager"
    },
    "videoFile": {
      "type": "string",
      "title": "動画ファイル",
      "format": "file-manager"
    }
  }
}
```

**表示**:
![Image from Gyazo](https://t.gyazo.com/teams/diverta/5ed04369d51e03be185f4928e692d767.png)

**特徴**:
- CKFinderとの完全統合
- ファイルパスを文字列として保存
- ファイル選択後にプレビューリンク表示

---

### WYSIWYGエディタ(HTMLエディタ)

```json
{
  "type": "object",
  "title": "記事コンテンツ",
  "properties": {
    "title": {
      "type": "string",
      "title": "タイトル",
      "minLength": 1,
      "maxLength": 100
    },
    "summary": {
      "type": "string",
      "title": "要約",
      "description": "記事の簡単な説明(プレーンテキスト)",
      "format": "textarea",
      "rows": 3
    },
    "content": {
      "type": "string",
      "title": "本文",
      "description": "記事の本文をHTML形式で入力してください",
      "format": "html"
    },
    "sidebarContent": {
      "type": "string",
      "title": "サイドバーコンテンツ",
      "description": "サイドバーに表示する内容",
      "format": "html",
      "format_options": { "height": 300 }
    }
  }
}
```

**表示**:
![Image from Gyazo](https://t.gyazo.com/teams/diverta/bfc0321c949722ac382fdc0dc232c5c2.png)

**特徴**:
- `format: "html"` でCKEditorを自動表示
- `format_options` でエディタのオプションを指定
- CKFinderとの完全統合(画像アップロード・管理)

<!--
- プレースホルダー機能対応
- 多言語対応(wysiwygConfig.langで言語設定)
-->

---

### 日付・日時の入力

```json
{
  "type": "object",
  "title": "日付・日時設定",
  "properties": {
    "publishDate": {
      "type": "string",
      "title": "公開日",
      "description": "記事を公開する日付を選択してください",
      "format": "date",
      "nullable": false
    },
    "publishDateTime": {
      "type": "string",
      "title": "公開日時",
      "description": "記事を公開する日時を選択してください",
      "format": "date-time",
      "nullable": false
    },
    "eventStartDate": {
      "type": ["string", "null"],
      "title": "イベント開始日(必須)",
      "description": "イベントの開始日を選択してください",
      "format": "date"
    },
    "eventEndDateTime": {
      "type": ["string", "null"],
      "title": "イベント終了日時(任意)",
      "description": "イベントの終了日時を選択してください",
      "format": "date-time"
    }
  }
}
```

**表示例**:
![Image from Gyazo](https://t.gyazo.com/teams/diverta/bb4ebac3e2685440a7454edbca4c6f89.png)

**特徴**:
- `format: "date"`: 日付のみ(年月日)を入力
- `format: "date-time"`: 日付と時刻(年月日 時分秒)を入力
- `nullable: false`: 必須項目の表示を設定可能
- DatePickerコンポーネントで直感的に選択

---

## 応用的な設定例

### 繰り返し項目(配列フォーム)

```json
{
  "type": "object",
  "properties": {
    "news": {
      "type": "array",
      "title": "ニュース一覧",
      "description": "最新のニュースを最大10件まで登録できます",
      "minItems": 1,
      "maxItems": 10,
      "items": {
        "type": "object",
        "properties": {
          "title": {
            "type": "string",
            "title": "タイトル",
            "description": "ニュースのタイトルを入力してください",
            "maxLength": 100
          },
          "link": {
            "type": "string",
            "title": "リンク",
            "description": "ニュース記事のURLを入力してください"
          },
          "publishDate": {
            "type": ["string", "null"],
            "title": "公開日",
            "format": "date"
          },
          "category": {
            "type": "string",
            "title": "カテゴリー",
            "enum": ["press", "blog", "event", "update"],
            "enumNames": ["プレスリリース", "ブログ", "イベント", "アップデート"]
          }
        },
        "required": ["title", "link"]
      }
    }
  }
}
```

**表示**: 繰り返し可能なカードUI
![Image from Gyazo](https://t.gyazo.com/teams/diverta/50336412cde1af120e01b402a5e26aa9.png)

**特徴**:
- 各アイテムがカード形式で表示
- ↑↓ボタンで順番を入れ替え可能
- 各カードの下に「追加」ボタンを配置
- `minItems`: 最低限必要なアイテム数(削除時に制限、アラート表示)
- `maxItems`: 最大登録可能数(追加時に制限、アラート表示)
- JSON プレビューボタンで確認可能(モーダル表示、読み取り専用)
- descriptionは各フィールドの下部に小さな文字で表示

---

### ブログ記事の設定フォーム例

```json
{
  "type": "object",
  "title": "ブログ記事設定",
  "properties": {
    "title": {
      "type": "string",
      "title": "記事タイトル",
      "minLength": 1,
      "maxLength": 100
    },
    "category": {
      "type": "string",
      "title": "カテゴリー",
      "enum": ["news", "tutorial", "blog", "announcement"],
      "enumNames": ["ニュース", "チュートリアル", "ブログ", "お知らせ"]
    },
    "status": {
      "type": "string",
      "title": "公開状態",
      "enum": ["draft", "review", "published", "archived"],
      "enumNames": ["下書き", "レビュー待ち", "公開済み", "アーカイブ"],
      "default": "draft"
    },
    "featured": {
      "type": "boolean",
      "title": "注目記事"
    },
    "tags": {
      "type": "array",
      "title": "タグ",
      "items": {
        "type": "string",
        "enum": ["Vue.js", "React", "Angular", "JavaScript", "TypeScript", "CSS", "HTML"]
      }
    },
    "visibility": {
      "type": "string",
      "title": "閲覧権限",
      "enum": ["public", "members", "premium", "private"],
      "enumNames": ["公開", "会員限定", "プレミアム会員限定", "非公開"],
      "default": "public"
    },
    "publishDate": {
      "type": ["string", "null"],
      "title": "公開日",
      "description": "記事を公開する日付を選択してください",
      "format": "date"
    },
    "publishDateTime": {
      "type": ["string", "null"],
      "title": "公開日時",
      "description": "記事を公開する日時を選択してください",
      "format": "date-time"
    }
  },
  "required": ["title", "category", "status"]
}
```

**表示例**:
![Image from Gyazo](https://t.gyazo.com/teams/diverta/c0d3b12391dea5d185523505db0c09ed.jpg)

---

### ECサイト商品設定例

```json
{
  "type": "object",
  "title": "商品設定",
  "properties": {
    "productName": {
      "type": "string",
      "title": "商品名",
      "minLength": 1
    },
    "size": {
      "type": "string",
      "title": "サイズ",
      "enum": ["XS", "S", "M", "L", "XL", "XXL"]
    },
    "color": {
      "type": "string",
      "title": "カラー",
      "enum": ["red", "blue", "green", "black", "white", "yellow"],
      "enumNames": ["レッド", "ブルー", "グリーン", "ブラック", "ホワイト", "イエロー"]
    },
    "availability": {
      "type": "string",
      "title": "在庫状況",
      "enum": ["in_stock", "low_stock", "out_of_stock", "pre_order"],
      "enumNames": ["在庫あり", "残りわずか", "在庫切れ", "予約受付中"],
      "default": "in_stock"
    },
    "shippingOptions": {
      "type": "array",
      "title": "配送方法",
      "items": {
        "type": "string",
        "enum": ["standard", "express", "overnight", "pickup"],
        "enumNames": ["通常配送", "速達", "翌日配送", "店舗受取"]
      }
    },
    "price": {
      "type": "number",
      "title": "価格",
      "minimum": 0
    },
    "onSale": {
      "type": "boolean",
      "title": "セール対象"
    }
  },
  "required": ["productName", "price", "availability"]
}
```

**表示例**:
![Image from Gyazo](https://t.gyazo.com/teams/diverta/be0b2e13412b6b80dbd110ea1ba3d399.png)

---

### より複雑な配列例(商品バリエーション with WYSIWYG)

```json
{
  "type": "object",
  "properties": {
    "productVariants": {
      "type": "array",
      "title": "商品バリエーション",
      "description": "サイズ・カラー別の商品情報を登録",
      "minItems": 1,
      "items": {
        "type": "object",
        "properties": {
          "sku": {
            "type": "string",
            "title": "SKU",
            "description": "商品コード"
          },
          "size": {
            "type": "string",
            "title": "サイズ",
            "enum": ["XS", "S", "M", "L", "XL", "XXL"]
          },
          "color": {
            "type": "string",
            "title": "カラー",
            "enum": ["red", "blue", "green", "black", "white"],
            "enumNames": ["レッド", "ブルー", "グリーン", "ブラック", "ホワイト"]
          },
          "description": {
            "type": "string",
            "title": "商品説明",
            "description": "このバリエーションの詳細説明",
            "format": "html",
            "format_options": { "height": 300 }
          },
          "stock": {
            "type": "integer",
            "title": "在庫数",
            "minimum": 0,
            "default": 0
          },
          "price": {
            "type": "number",
            "title": "価格",
            "minimum": 0
          },
          "image": {
            "type": "string",
            "title": "商品画像",
            "format": "file-manager"
          },
          "available": {
            "type": "boolean",
            "title": "販売中",
            "default": true
          }
        },
        "required": ["sku", "size", "color", "price"]
      }
    }
  }
}
```

**表示例**:
![Image from Gyazo](https://t.gyazo.com/teams/diverta/26bb96f163b26aaf7198730ce22e36ce.jpg)

**特徴**:
- 配列オブジェクト内で`format: "html"`を使用してWYSIWYGエディタを配置可能
- 各アイテムごとに独立したCKEditorインスタンスが生成される
- ファイルマネージャーとWYSIWYGを同時に使用可能
- `format_options`でエディタの高さを個別に調整可能

## 設定リファレンス

### 表示モード
- `JSONデータ定義からUIを構成する`が有効 → JSON Schemaに基づいた**フォームUI**を表示
- `JSONデータ定義からUIを構成する`が無効 → **JSONエディタ**のみ表示

### フィールドタイプの自動判定
- `string` - テキスト入力
- `string` + `enum` - セレクトボックス
- `string` + `format: "date"` - DatePicker(日付のみ)
- `string` + `format: "date-time"` - DatePicker(日時)
- `string` + `format: "file-manager"` - ファイルマネージャー(CKFinder統合)
- `string` + `format: "html"` - WYSIWYGエディタ(CKEditor)
- `string` + `format: "textarea"` - テキストエリア
- `number` / `integer` - 数値入力
- `number` / `integer` + `enum` - 数値セレクトボックス
- `boolean` - チェックボックス
- `array` + `items.type: "string"` - カンマ区切りテキストエリア
- `array` + `items.enum` - 複数選択セレクトボックス
- `array` + `items.type: "object"` - 繰り返しカードUI
- `object` - JSON形式テキストエリア

### デフォルト値の決定
1. `default`が設定されている → その値を使用
2. `default`が未設定の場合:
   - `enum`に`null`が含まれる → `null`を初期値とする
   - `enum`に`null`が含まれない → `enum[0]`(最初の値)を初期値とする

### バリデーション
- `required` - 必須項目(Array/Objectタイプ用)
- `nullable` - null許可(false時に必須マーク表示、Enum/Date/Number/Integer用)
- `minLength` / `maxLength` - 文字数制限(String用、minLength: 1で必須マーク表示)
- `minimum` / `maximum` - 数値範囲
- `minItems` / `maxItems` - 配列要素数制限
- `pattern` - 正規表現パターン

### 必須項目の設定方法

フィールドを必須項目にする方法は、フィールドタイプによって異なります。

#### Enum、Date、Date-time、Number、Integerの場合
```json
{
  "type": "string",  // または "integer", "number"、nullを許可しない
  "format": "date",  // または "enum": ["1", "2"],
  "nullable": false  // ← これがfalseの場合に必須マーク表示
}
```

**ルール**: `nullable: false` を設定すると必須マーク(*)が表示されます。typeでnullを許可しないことで入力必須にします。

#### String(テキスト入力)の場合
```json
{
  "type": "string",
  "minLength": 1  // ← これが1以上の場合に必須マーク表示
}
```

**ルール**: `minLength: 1` 以上を設定すると必須項目になり、必須マーク(*)が表示されます。

#### Array(配列)、Object(オブジェクト)の場合
```json
{
  "type": "object",
  "properties": {
    "tags": {
      "type": "array",
      "minItems": 1,  // ← 1つ以上選択必須
      "items": {
        "type": "string",
        "enum": ["1", "2", "3"]
      }
    }
  },
  "required": ["tags"]  // ← required配列に含まれる場合に必須マーク表示
}
```

**ルール**: スキーマのトップレベルにある`required`配列にフィールド名を含めると必須表示になります。

#### 実践例: 複数タイプの必須項目を含むスキーマ

```json
{
  "type": "object",
  "properties": {
    "status": {
      "type": "string",  // ← "null"を許可しない
      "enum": [null, "draft", "published"],
      "nullable": false  // ← 必須マーク表示
    },
    "name": {
      "type": "string",
      "minLength": 1  // ← 必須マーク表示
    },
    "publishDate": {
      "type": "string",  // ← "null"を許可しない
      "format": "date",
      "nullable": false  // ← 必須マーク表示
    },
    "tags": {
      "type": "array",
      "minItems": 1,  // ← 1つ以上選択必須
      "items": {
        "type": "string",
        "enum": ["1", "2", "3"]
      }
    }
  },
  "required": ["tags"]  // ← Array/Object以外は各プロパティの設定で必須判定される
}
```

### 配列オブジェクトUIの動作
- アイテム追加時に `maxItems` チェック(上限に達している場合はアラート表示)
- アイテム削除時に `minItems` チェック(最小数を下回る場合はアラート表示)
- 各カード下部に「追加」ボタンを配置
- アイテムがゼロの場合は最初の「追加」ボタンのみ表示

### その他の機能
- `enumNames`で表示ラベルをカスタマイズ可能
- `type: ["string", "null"]`のようなUnion Type(nullable)に対応
- `description`はフィールド下部にヘルプテキストとして表示
- JSON プレビューボタンで現在の値を確認可能(モーダル表示)

### ネスト構造の対応状況と制限

#### サポートされているネスト構造

システムは以下のネスト構造に対応しています：

##### Object (単一オブジェクト)
```json
{
  "type": "object",
  "properties": {
    "name": {"type": "string"},
    "email": {"type": "string"}
  }
}
```

##### Array → Object (オブジェクトの配列)
```json
{
  "type": "array",
  "items": {
    "type": "object",
    "properties": {
      "title": {"type": "string"},
      "link": {"type": "string"}
    }
  }
}
```

##### Object → Array → Object (オブジェクト内の配列内のオブジェクト)
```json
{
  "type": "object",
  "properties": {
    "news": {
      "type": "array",
      "items": {
        "type": "object",
        "properties": {
          "title": {"type": "string"},
          "category": {"type": "string", "enum": ["blog", "news"]}
        }
      }
    }
  }
}
```

**解説**: これらのパターンは完全にサポートされており、UIで直接編集可能です。

---

#### 制限事項: Array → Object → Array → Object

**4階層以上のネスト構造(Array → Object → Array → Object)は、現在UIでの直接編集に対応していません。**

##### 対応していない例

```json
{
  "type": "object",
  "properties": {
    "departments": {
      "type": "array",
      "title": "部署一覧",
      "items": {
        "type": "object",
        "properties": {
          "departmentName": {"type": "string", "title": "部署名"},
          "employees": {
            "type": "array",
            "title": "社員一覧",
            "items": {
              "type": "object",
              "properties": {
                "employeeName": {"type": "string", "title": "社員名"},
                "skills": {
                  "type": "array",
                  "title": "スキル一覧",
                  "items": {
                    "type": "object",
                    "properties": {
                      "skillName": {"type": "string"}
                    }
                  }
                }
              }
            }
          }
        }
      }
    }
  }
}
```

**問題点**: `departments` (Array) → `employees` (Object → Array) → `skills` (Object → Array) のような4階層構造は、繰り返しUIの中に繰り返しUIがネストされるため、現在対応していません。

---

#### 推奨される回避策: 構造を分割する

深いネスト構造が必要な場合は、**複数の拡張項目に分割**することで対応できます。

##### 解決例: 2つの拡張項目に分割

###### 拡張項目1: `departments_data` (部署と社員情報)
```json
{
  "type": "object",
  "properties": {
    "departmentId": {
      "type": "string",
      "title": "部署ID",
      "description": "一意の部署識別子"
    },
    "departmentName": {
      "type": "string",
      "title": "部署名"
    },
    "employees": {
      "type": "array",
      "title": "社員一覧",
      "items": {
        "type": "object",
        "properties": {
          "employeeId": {
            "type": "string",
            "title": "社員ID"
          },
          "employeeName": {
            "type": "string",
            "title": "社員名"
          },
          "position": {
            "type": "string",
            "title": "役職",
            "enum": ["manager","staff","intern"],
            "enumNames": ["マネージャー","スタッフ","インターン"
            ]
          }
        }
      }
    }
  }
}
```

###### 拡張項目2: `employee_skills_data` (社員のスキル情報)
```json
{
  "type": "object",
  "properties": {
    "employeeId": {
      "type": "string",
      "title": "社員ID",
      "description": "departments_dataの社員IDと対応"
    },
    "employeeName": {
      "type": "string",
      "title": "社員名(参照用)"
    },
    "employeeSkills": {
      "type": "array",
      "title": "社員スキル一覧",
      "items": {
        "type": "object",
        "properties": {
          "skillName": {
            "type": "string",
            "title": "スキル名"
          },
          "level": {
            "type": "string",
            "title": "習熟度",
            "enum": ["beginner","intermediate","advanced","expert"],
            "enumNames": ["初級","中級","上級","エキスパート"]
          },
          "yearsOfExperience": {
            "type": "integer",
            "title": "経験年数",
            "minimum": 0
          }
        }
      }
    }
  }
}
```

**メリット**:
- ✅ 各拡張項目が3階層以内(Object → Array → Object)に収まる
- ✅ UIで完全に編集可能
- ✅ `employeeId`をキーとしてデータを関連付け
- ✅ それぞれのデータを独立して管理できる

**データの関連付け**:
- `departments_data`で社員の基本情報を管理
- `employee_skills_data`で社員のスキル情報を管理
- 両者を`employeeId`で紐付け

---

#### ネスト構造の対応表

| 構造パターン | 階層数 | UI対応 | 例 |
|------------|--------|--------|-----|
| Object | 1階層 | ✅ | `{type: "object"}` |
| Array → Object | 2階層 | ✅ | ニュース一覧、商品バリエーション |
| Object → Array → Object | 3階層 | ✅ | ユーザー情報 → 注文履歴 |
| Array → Object → Array → Object | 4階層 | ❌ | 部署 → 社員 → スキル |
| それ以上の深いネスト | 5階層以上 | ❌ | - |

