PHP JSON

JSON (JavaScript Object Notation) 是輕量級的資料交換格式,PHP 內建支援 JSON 編碼和解碼。

編碼 (json_encode)

<?php
// 陣列轉 JSON
$data = ['name' => 'Alice', 'age' => 25];
$json = json_encode($data);
echo $json;  // {"name":"Alice","age":25}

// 索引陣列
$arr = ['apple', 'banana', 'cherry'];
echo json_encode($arr);  // ["apple","banana","cherry"]
?>

編碼選項

json_encode() 的第二個參數可以傳入選項來調整輸出格式。多個選項可以用 | 運算符組合:

<?php
$data = ['name' => '小明', 'path' => '/home/user'];

// 預設輸出
echo json_encode($data);
// {"name":"\u5c0f\u660e","path":"\/home\/user"}

// 不跳脫 Unicode
echo json_encode($data, JSON_UNESCAPED_UNICODE);
// {"name":"小明","path":"\/home\/user"}

// 不跳脫斜線
echo json_encode($data, JSON_UNESCAPED_SLASHES);
// {"name":"\u5c0f\u660e","path":"/home/user"}

// 美化輸出
echo json_encode($data, JSON_PRETTY_PRINT);

// 組合選項
echo json_encode($data, 
    JSON_UNESCAPED_UNICODE | 
    JSON_UNESCAPED_SLASHES | 
    JSON_PRETTY_PRINT
);
?>

解碼 (json_decode)

json_decode() 將 JSON 字串轉換回 PHP 資料結構。第二個參數決定要轉成物件(預設)還是關聯陣列:

<?php
$json = '{"name":"Alice","age":25}';

// 解碼為物件(預設)
$obj = json_decode($json);
echo $obj->name;  // Alice

// 解碼為關聯陣列
$arr = json_decode($json, true);
echo $arr['name'];  // Alice

// 解碼深度和選項
$data = json_decode($json, true, 512, JSON_THROW_ON_ERROR);
?>

錯誤處理

當 JSON 格式錯誤時,json_decode() 預設會回傳 null 而不是拋出例外。有兩種方式可以偵測錯誤:

<?php
$invalidJson = '{"name": "Alice",}';  // 無效的 JSON

// 方法一:檢查 json_last_error()
$data = json_decode($invalidJson);
if (json_last_error() !== JSON_ERROR_NONE) {
    echo "JSON 錯誤:" . json_last_error_msg();
}

// 方法二:使用 JSON_THROW_ON_ERROR(PHP 7.3+)
try {
    $data = json_decode($invalidJson, true, 512, JSON_THROW_ON_ERROR);
} catch (JsonException $e) {
    echo "JSON 錯誤:" . $e->getMessage();
}
?>

常見錯誤碼

常數說明
JSON_ERROR_NONE沒有錯誤
JSON_ERROR_SYNTAX語法錯誤
JSON_ERROR_DEPTH超過最大深度
JSON_ERROR_UTF8UTF-8 編碼錯誤

驗證 JSON

<?php
function isValidJson(string $string): bool {
    json_decode($string);
    return json_last_error() === JSON_ERROR_NONE;
}

echo isValidJson('{"name":"Alice"}') ? 'Valid' : 'Invalid';  // Valid
echo isValidJson('invalid') ? 'Valid' : 'Invalid';            // Invalid
?>

處理 JSON 檔案

<?php
// 讀取 JSON 檔案
$json = file_get_contents('data.json');
$data = json_decode($json, true);

// 寫入 JSON 檔案
$data = ['users' => [
    ['name' => 'Alice', 'age' => 25],
    ['name' => 'Bob', 'age' => 30],
]];
file_put_contents(
    'data.json', 
    json_encode($data, JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE)
);
?>

API 回應

<?php
// 設定 Content-Type
header('Content-Type: application/json; charset=utf-8');

// 成功回應
function jsonResponse($data, int $status = 200): void {
    http_response_code($status);
    echo json_encode([
        'success' => true,
        'data' => $data,
    ], JSON_UNESCAPED_UNICODE);
    exit;
}

// 錯誤回應
function jsonError(string $message, int $status = 400): void {
    http_response_code($status);
    echo json_encode([
        'success' => false,
        'error' => $message,
    ], JSON_UNESCAPED_UNICODE);
    exit;
}

// 使用
$users = [
    ['id' => 1, 'name' => 'Alice'],
    ['id' => 2, 'name' => 'Bob'],
];
jsonResponse($users);
?>

JsonSerializable 介面

實作 JsonSerializable 介面可以自訂物件轉換成 JSON 時的輸出內容,這在需要隱藏某些屬性(如密碼)或自訂格式時非常有用:

<?php
class User implements JsonSerializable {
    public function __construct(
        private int $id,
        private string $name,
        private string $email,
        private string $password  // 不想輸出
    ) {}
    
    public function jsonSerialize(): array {
        return [
            'id' => $this->id,
            'name' => $this->name,
            'email' => $this->email,
            // password 不包含
        ];
    }
}

$user = new User(1, 'Alice', 'alice@example.com', 'secret');
echo json_encode($user);
// {"id":1,"name":"Alice","email":"alice@example.com"}
?>