首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> 網頁程式設計 >> PHP 程式設計 >> PHP 綜合知識

PHP JSON 處理完整指南:json_encode/decode 深度解析、常見陷阱與效能優化

     編輯:PHP 綜合知識

JSON 是現代 Web API 的標準資料交換格式。PHP 5.2+ 內建 json_encode() / json_decode(),本文深度解析其運作細節、陣列/物件轉換規則、Unicode 處理、常見錯誤與最佳實踐。

一、json_encode() 核心行為

基本轉換對照表

PHP 型別JSON 型別範例
陣列 (索引式,0 起始連續)Array [][1,2,3]
陣列 (關聯式/非連續索引)Object {}{"a":1,"b":2}
物件 (public 屬性)Object {}{"name":"John"}
字串String"hello"
整數/浮點數Number42, 3.14
true/falsetrue/falsetrue
nullnullnull
resource / 閉包❌ 無法編碼回傳 false 並觸發警告

關鍵選項 (bitmask)

<?php
$data = ['name' => '王小明', 'age' => 25, 'tags' => ['php', 'json']];

// JSON_UNESCAPED_UNICODE: 不轉義中文 (預設會變 \uxxxx)
echo json_encode($data, JSON_UNESCAPED_UNICODE);
// {"name":"王小明","age":25,"tags":["php","json"]}

// JSON_UNESCAPED_SLASHES: 不轉義 /
echo json_encode(['url' => 'https://example.com'], JSON_UNESCAPED_SLASHES);

// JSON_NUMERIC_CHECK: 數值字串轉數字
echo json_encode(['id' => '123', 'price' => '99.9'], JSON_NUMERIC_CHECK);
// {"id":123,"price":99.9}

// JSON_PRETTY_PRINT: 格式化輸出 (除錯用)
echo json_encode($data, JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE);

/*
{
    "name": "王小明",
    "age": 25,
    "tags": [
        "php",
        "json"
    ]
}
*/

// JSON_FORCE_OBJECT: 強制陣列輸出為物件
echo json_encode([1,2,3], JSON_FORCE_OBJECT);
// {"0":1,"1":2,"2":3}

// JSON_PRESERVE_ZERO_FRACTION: 保留浮點數 .0
echo json_encode(1.0, JSON_PRESERVE_ZERO_FRACTION); // 1.0

// JSON_THROW_ON_ERROR: 失敗拋出例外 (PHP 7.3+)
try {
    json_encode($resource, JSON_THROW_ON_ERROR);
} catch (JsonException $e) {
    // 處理錯誤
}
?>

二、索引陣列 vs 關聯陣列判斷邏輯

<?php
// 連續數字索引 0,1,2... -> JSON 陣列
json_encode(['a', 'b', 'c']);        // ["a","b","c"]
json_encode([0=>'a', 1=>'b', 2=>'c']); // ["a","b","c"]

// 非連續、非 0 起始、字串鍵 -> JSON 物件
json_encode([1=>'a', 2=>'b']);       // {"1":"a","2":"b"}
json_encode(['a'=>1, 'b'=>2]);       // {"a":1,"b":2}
json_encode([0=>'a', 2=>'c']);       // {"0":"a","2":"c"}  (索引不連續)

// 強制輸出為陣列:array_values()
json_encode(array_values([1=>'a', 2=>'b'])); // ["a","b"]
?>

三、json_decode() 核心行為

<?php
$json = '{"name":"王小明","age":25,"skills":["PHP","JS"],"active":true}';

// 預設回傳 stdClass 物件
$obj = json_decode($json);
echo $obj->name;        // 王小明
echo $obj->skills[0];   // PHP

// 第二參數 true -> 關聯陣列
$arr = json_decode($json, true);
echo $arr['name'];      // 王小明

// 第三參數:最大深度 (預設 512)
$deep = json_decode($very_deep_json, true, 10);

// 第四參數:選項 (PHP 7.2+)
$opts = JSON_THROW_ON_ERROR | JSON_BIGINT_AS_STRING;
$arr = json_decode($json, true, 512, $opts);
?>

JSON_BIGINT_AS_STRING:避免大整數精度遺失

<?php
$json = '{"id": 12345678901234567890}';

// 預設:PHP 整數溢位轉浮點,精度遺失
var_dump(json_decode($json, true));
// ['id' => 1.2345678901235E+19]

// 保持字串
var_dump(json_decode($json, true, 512, JSON_BIGINT_AS_STRING));
// ['id' => '12345678901234567890']
?>

四、常見錯誤與除錯

1. JSON 語法錯誤 (json_last_error)

<?php
$bad_json = "{ 'name': 'test' }";  // 單引號不合法
$result = json_decode($bad_json);
var_dump($result);  // null

// 檢查錯誤
switch (json_last_error()) {
    case JSON_ERROR_NONE:        echo '無錯誤'; break;
    case JSON_ERROR_DEPTH:       echo '超過最大深度'; break;
    case JSON_ERROR_STATE_MISMATCH: echo '狀態不匹配'; break;
    case JSON_ERROR_CTRL_CHAR:   echo '控制字元錯誤'; break;
    case JSON_ERROR_SYNTAX:      echo '語法錯誤'; break;
    case JSON_ERROR_UTF8:        echo 'UTF-8 編碼錯誤'; break;
    case JSON_ERROR_RECURSION:   echo '遞迴參考'; break;
    case JSON_ERROR_INF_OR_NAN:  echo 'Inf/NaN'; break;
    case JSON_ERROR_UNSUPPORTED_TYPE: echo '不支援型別'; break;
    case JSON_ERROR_INVALID_PROPERTY_NAME: echo '屬性名無效'; break;
    case JSON_ERROR_UTF16:       echo 'UTF-16 編碼錯誤'; break;
}

// PHP 7.3+ 可用 JSON_THROW_ON_ERROR 直接拋出 JsonException
?>

2. UTF-8 編碼問題

<?php
// 輸入必須是 UTF-8
$str = '中文';  // 假設檔案是 UTF-8 編碼
json_encode($str);  // 正常

// 若輸入是 Big5/GB2312,需先轉碼
$big5_str = iconv('BIG5', 'UTF-8', $big5_str);
json_encode($big5_str);

// 解碼時同理:json_decode 回傳 UTF-8 字串
$arr = json_decode($json, true);
// 若需輸出 Big5:iconv('UTF-8', 'BIG5', $arr['field'])
?>

3. 循環參考

<?php
$a = ['name' => 'A'];
$b = ['name' => 'B', 'ref' => &$a];
$a['ref'] = &$b;

json_encode($a);  // 警告:Recursion detected,回傳 false

// 解決:實作 JsonSerializable 介面控制輸出
class Node implements JsonSerializable {
    public $id;
    public $children = [];
    private $parent;  // 不序列化

    public function jsonSerialize() {
        return ['id' => $this->id, 'children' => $this->children];
    }
}
?>

五、效能優化

1. 避免重複編碼/解碼

<?php
// 壞:每次請求都 encode
echo json_encode($big_array);

// 好:快取編碼結果
$cache_key = 'json:' . md5(serialize($big_array));
$json = $redis->get($cache_key);
if (!$json) {
    $json = json_encode($big_array, JSON_UNESCAPED_UNICODE);
    $redis->setex($cache_key, 3600, $json);
}
echo $json;
?>

2. 大量資料串流輸出

<?php
// 避免一次編碼超大陣列佔用記憶體
function streamJsonArray($items) {
    echo '[';
    $first = true;
    foreach ($items as $item) {
        if (!$first) echo ',';
        echo json_encode($item, JSON_UNESCAPED_UNICODE);
        $first = false;
    }
    echo ']';
}

// 使用 Generator 記憶體友善
function getLargeDataset() {
    for ($i = 0; $i < 1000000; $i++) {
        yield ['id' => $i, 'value' => 'data'];
    }
}

streamJsonArray(getLargeDataset());
?>

3. 擴充套件:更快的 JSON

六、最佳實踐清單

  1. API 統一輸出 UTF-8 JSON:Header Content-Type: application/json; charset=utf-8
  2. 永遠加上 JSON_UNESCAPED_UNICODE:中文可讀、體積更小
  3. 解碼指定 true 回傳陣列:json_decode($json, true) 避免 stdClass 操作麻煩
  4. 大整數欄位用 JSON_BIGINT_AS_STRING:避免 ID 精度遺失
  5. 錯誤處理用 JSON_THROW_ON_ERROR:PHP 7.3+ 統一例外處理
  6. 不要手工拼 JSON 字串:永遠用 json_encode() 確保合法
  7. 資料庫 JSON 欄位:MySQL 5.7+ / PostgreSQL 9.4+ 原生支援 JSON 型別與索引

掌握 JSON 細節,能避免 API 整合時的大量除錯時間,也能寫出更健壯的資料交換介面。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved
擴充特色安裝
json (內建)標準、穩定預設
jsond更嚴格 RFC 8259 相容pecl install jsond
simdjsonSIMD 加速,極快 (PHP 8.1+)pecl install simdjson