自 PHP 5.2 起,json_encode() 與 json_decode() 成為原生擴展,是現代 PHP 開發中資料交換、API 介接、配置儲存的核心工具。本文系統整理 JSON 編碼/解碼的進階特性、索引陣列與關聯陣列的轉換差異、類別屬性序列化行為、強制輸出格式技巧,以及 json_decode 常見失敗原因與解決方案。
<?php
$arr = ['a'=>1, 'b'=>2, 'c'=>3, 'd'=>4, 'e'=>5];
echo json_encode($arr);
// 輸出:{"a":1,"b":2,"c":3,"d":4,"e":5}
?>
<?php
$obj = new stdClass();
$obj->body = 'another post';
$obj->id = 21;
$obj->approved = true;
$obj->favorite_count = 1;
$obj->status = null;
echo json_encode($obj);
// 輸出:
// {
// "body": "another post",
// "id": 21,
// "approved": true,
// "favorite_count": 1,
// "status": null
// }
?>
關鍵點: json_encode 僅序列化 public 屬性。private、protected、常數、方法皆會被忽略。
注意: JSON 標準強制要求 UTF-8 編碼。若輸入資料包含非 UTF-8 字元(如 GB2312 中文、ISO-8859-1 西文),json_encode會返回null或空字串。請在編碼前使用mb_convert_encoding()或iconv()轉換為 UTF-8。
JavaScript 無關聯陣列概念,因此 PHP 的 json_encode 採用以下規則:
[...]{...}<?php
// 索引陣列
$indexed = ['one', 'two', 'three'];
echo json_encode($indexed); // ["one","two","three"]
// 關聯陣列(字串鍵)
$assoc = ['1'=>'one', '2'=>'two', '3'=>'three'];
echo json_encode($assoc); // {"1":"one","2":"two","3":"three"}
// 關聯陣列(非連續數字鍵)
$gap = [0=>'a', 2=>'b', 5=>'c'];
echo json_encode($gap); // {"0":"a","2":"b","5":"c"}
?>
<?php
$arr = ['one', 'two', 'three'];
// 方法 1:型別轉換
echo json_encode((object)$arr); // {"0":"one","1":"two","2":"three"}
// 方法 2:JSON_FORCE_OBJECT 旗標(PHP 5.3+)
echo json_encode($arr, JSON_FORCE_OBJECT); // 同上
?>
<?php
class Foo {
const ERROR_CODE = '404';
public $public_ex = 'this is public';
private $private_ex = 'this is private!';
protected $protected_ex = 'this should be protected';
public function getErrorCode() {
return self::ERROR_CODE;
}
}
$foo = new Foo();
echo json_encode($foo);
// 輸出:{"public_ex":"this is public"}
?>
序列化規則總結:
public 實例屬性 → 包含private/protected 屬性 → 忽略const) → 忽略static) → 忽略對策: 若需序列化受保護屬性,實作 JsonSerializable 介面並自訂 jsonSerialize() 方法。
<?php
$json = '{"a":1,"b":2,"c":3,"d":4,"e":5}';
$obj = json_decode($json);
echo $obj->a; // 1
var_dump($obj); // object(stdClass)#1 (5) { ["a"]=>int(1) ... }
?>
<?php
$arr = json_decode($json, true);
var_dump($arr);
// array(5) { ["a"]=>int(1) ["b"]=>int(2) ... }
?>
| 錯誤 JSON 範例 | 錯誤原因 | 修正 |
|---|---|---|
{ 'bar': 'baz' } | 字串分隔符必須用雙引號,不可用單引號 | { "bar": "baz" } |
{ bar: "baz" } | 鍵名(冒號左側)必須用雙引號包裹 | { "bar": "baz" } |
{ "bar": "baz", } | 最後一個鍵值對後不能有尾隨逗號 | { "bar": "baz" } |
"Hello World" | JSON 頂層必須是物件或陣列,純字串/數值無效 | 包裹為 ["Hello World"] 或 {"msg":"Hello World"} |
12345 | 同上,頂層不可為裸露數值 | 包裹為陣列或物件 |
<?php
$json = '...'; // 來源資料
$data = json_decode($json, true);
if (json_last_error() !== JSON_ERROR_NONE) {
// 詳細錯誤訊息
$errors = [
JSON_ERROR_DEPTH => '堆疊深度超過限制',
JSON_ERROR_STATE_MISMATCH => '狀態不匹配(無效或畸形 JSON)',
JSON_ERROR_CTRL_CHAR => '控制字元錯誤',
JSON_ERROR_SYNTAX => '語法錯誤(最常見)',
JSON_ERROR_UTF8 => '非 UTF-8 編碼字元',
];
throw new Exception('JSON 解析失敗:' . ($errors[json_last_error()] ?? '未知錯誤'));
}
?>
json_encode:注意 UTF-8 編碼、關聯陣列輸出為物件、僅序列化 public 屬性。json_decode:預設回物件,加 true 回關聯陣列;嚴格遵循 JSON 標準(雙引號、無尾隨逗號、頂層為物件/陣列)。json_last_error() 與 json_last_error_msg()(PHP 5.5+)快速定位問題。JsonSerializable 介面,掌控輸出結構。