JSON (JavaScript Object Notation) 僅定義兩種資料結構:物件 (Object) 與陣列 (Array)。理解其語意差異、序列化規則與各語言對應關係,是前後端介接、API 設計、資料建模的基礎。本文系統性解析 JSON 雙重結構體系。
// 只有 4 種結構規則:
// 1. 並列資料用逗號分隔: "a": 1, "b": 2
// 2. 映射關係用冒號: "key": "value"
// 3. 陣列用方括號: [1, 2, 3]
// 4. 物件用大括號: {"key": "value"}
// 7 種資料型別:
// 字串、數值、布林、null、陣列、物件
| 維度 | 物件 {} | 陣列 [] |
|---|---|---|
| 語意 | 無序鍵值集合 (Mapping) | 有序值序列 (Sequence) |
| 鍵/索引 | 字串鍵 (Key) | 整數索引 (Index: 0,1,2...) |
| 順序性 | 語意上無序 (實作常保留插入序) | 嚴格有序 |
| 存取方式 | obj.key / obj['key'] | arr[0], arr[1] |
| 適用場景 | 實體屬性、字典、設定 | 列表、集合、時間序列 |
| 重複鍵 | 不允許 (最後勝出) | 允許重複值 |
// 索引陣列 (0 起始、連續) -> JSON 陣列
$indexed = [111, 'aaa', 'bbb'];
json_encode($indexed); // [111,"aaa","bbb"]
// 關聯陣列 (字串鍵/非連續/非 0 起始) -> JSON 物件
$assoc = ['a' => 'aaa', 'b' => 222];
json_encode($assoc); // {"a":"aaa","b":222}
// 混合/破壞連續性 -> JSON 物件
$broken = [0 => 'a', 2 => 'b']; // 缺少 1
json_encode($broken); // {"0":"a","2":"b"}
// 強制輸出
json_encode([1,2], JSON_FORCE_OBJECT); // {"0":1,"1":2}
json_encode((object)[1,2]); // {"0":1,"1":2}
$data = [
'user' => [
'id' => 123,
'name' => 'John',
'roles' => ['admin', 'editor'], // 陣列
'profile' => [ // 物件
'avatar' => 'url',
'bio' => 'Developer'
]
],
'posts' => [ // 陣列包含物件
['id' => 1, 'title' => 'Post 1'],
['id' => 2, 'title' => 'Post 2'],
]
];
// JSON 輸出:
// {
// "user": {
// "id": 123,
// "name": "John",
// "roles": ["admin", "editor"],
// "profile": {"avatar": "url", "bio": "Developer"}
// },
// "posts": [
// {"id": 1, "title": "Post 1"},
// {"id": 2, "title": "Post 2"}
// ]
// }
| 語言 | JSON 物件 {} | JSON 陣列 [] |
|---|---|---|
| JavaScript | Object / Map | Array |
| PHP | 關聯陣列 / stdClass | 索引陣列 |
| Python | dict | list |
| Java | Map / JSONObject | List / JSONArray |
| Go | map[string]interface{} / struct | slice / []interface{} |
| C# | Dictionary / JObject | List / JArray |
$json = '{"a":1,"b":[2,3],"c":{"d":4}}';
// 預設:物件轉為 stdClass
$obj = json_decode($json);
// $obj->a = 1
// $obj->b = stdClass (但實際是 array! PHP 特殊處理)
// $obj->c = stdClass
// true 參數:全部轉為關聯陣列
$arr = json_decode($json, true);
// $arr['a'] = 1
// $arr['b'] = [2, 3]
// $arr['c'] = ['d' => 4]
// 深度限制 (PHP 5.3+)
json_decode($json, false, 2); // 最大深度 2
// 大整數處理 (PHP 7.1+)
json_decode('{"id": 1234567890123456789}', false, 512, JSON_BIGINT_AS_STRING);
// 避免精度損失,回傳字串
[...],即使 0 或 1 筆資料{...}{"key": "value"}{"code": 400, "message": "...", "details": {...}}{"data": [...], "pagination": {"page": 1, "per_page": 20, "total": 100}}// ❌ 錯誤:單筆資源回傳陣列
GET /users/123
[{"id": 123, "name": "John"}] // 前端需寫 data[0].name
// ✅ 正確:回傳物件
{"id": 123, "name": "John"}
// ❌ 錯誤:集合回傳物件 (鍵為 ID)
{"123": {"name": "John"}, "456": {"name": "Jane"}}
// 無法保證順序、難以迭代、擴展性差
// ✅ 正確:集合回傳陣列
[{"id": 123, "name": "John"}, {"id": 456, "name": "Jane"}]
{
"$schema": "https://json-schema.org/draft-07/schema#",
"type": "object",
"properties": {
"data": {
"type": "array",
"items": {
"type": "object",
"required": ["id", "name"],
"properties": {
"id": {"type": "integer"},
"name": {"type": "string"},
"email": {"type": "string", "format": "email"}
}
}
},
"pagination": {
"type": "object",
"properties": {
"page": {"type": "integer", "minimum": 1},
"per_page": {"type": "integer", "minimum": 1},
"total": {"type": "integer", "minimum": 0}
}
}
}
}
JSON 看似簡單,但其物件/陣列語意邊界直接影響 API 易用性、前端程式碼複雜度與跨語言相容性。嚴格遵循「集合用陣列、實體用物件、映射用物件」原則,能構建直觀、穩健的資料介面。