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

PHP json_encode 陷阱:數字索引陣列被序列化為 JSON Array 而非 Object 的根因與對策

     編輯:PHP 相關知識

現象

<?php
$a = ['Jack', 'Sam', 'Tom'];
echo json_encode($a);
// 輸出:["Jack","Sam","Tom"]  ← JSON Array

$b = ['0' => 'Jack', '1' => 'Sam', '2' => 'Tom'];
echo json_encode($b);
// 輸出:["Jack","Sam","Tom"]  ← 依然是 JSON Array

$c = ['person-1' => 'Jack', 'person-2' => 'Sam'];
echo json_encode($c);
// 輸出:{"person-1":"Jack","person-2":"Sam"}  ← JSON Object

根因:PHP 陣列的雙重身份與 json_encode 判斷邏輯

PHP 陣列同時是「有序映射」與「列表」。json_encode 依據以下規則決定輸出:

  1. 若陣列鍵為 連續整數從 0 開始(0, 1, 2, ... n-1)→ 輸出 JSON Array
  2. 否則(字串鍵、非連續整數、負數、不從 0 開始)→ 輸出 JSON Object

這符合 JSON 標準:Array 必須為零基索引有序列表;Object 為鍵值對集合。

前端接收端的實際影響

PHP 輸出JS 解析後可用操作
JSON ArrayJS Array ['Jack','Sam','Tom'].length, [0], .map(), .forEach()
JSON ObjectJS Object {'person-1':'Jack',...}['person-1'], Object.keys(), for...in

若前端期望 Object 卻收到 Array(或反之),直接導致 Cannot read property 'length' of undefined 或鍵遍歷失效。

強制輸出 JSON Object 的三種方案

1. 使用 JSON_FORCE_OBJECT 旗標(最簡單)

<?php
echo json_encode($a, JSON_FORCE_OBJECT);
// {"0":"Jack","1":"Sam","2":"Tom"}

缺點:鍵變為字串 "0",非語義化鍵。

2. 重構為關聯陣列(語義化,推薦)

<?php
// 方案 A:手動指定鍵
$arr = ['user_1' => 'Jack', 'user_2' => 'Sam', 'user_3' => 'Tom'];

// 方案 B:自動生成語義鍵
$arr = array_combine(
    array_map(fn($i) => "user_{$i}", range(1, count($a))),
    $a
);
// 輸出:{"user_1":"Jack","user_2":"Sam","user_3":"Tom"}

3. 包裝為標準結構(API 規範化)

<?php
// 統一回應格式
$response = [
    'code' => 0,
    'msg'  => 'ok',
    'data' => [
        'list'  => $a,           // 這裡保持 Array 語義
        'total' => count($a),
    ],
];
echo json_encode($response);
// {"code":0,"msg":"ok","data":{"list":["Jack","Sam","Tom"],"total":3}}

此法將「列表語義」保留在 data.list,外層永遠為 Object,前端統一解構 res.data.list,最穩健。

反向陷阱:前端傳 Array,PHP 收到 Object

// JS
fetch('/api', {
    method: 'POST',
    headers: {'Content-Type': 'application/json'},
    body: JSON.stringify(['a', 'b', 'c']) // 發送 Array
});

// PHP
$data = json_decode(file_get_contents('php://input'), true);
// $data 為 ['a','b','c'] ← 正確還原為 PHP 索引陣列

// 但若 JS 發送 {0:'a',1:'b',2:'c'}(Object)
// PHP 同樣解碼為 ['a','b','c'] ← 因為 json_decode 第二參數 true 強制關聯陣列

json_decode($json, true) 總是返回 PHP 陣列(關聯陣列),無法區分原始 JSON Array/Object。若需保留類型資訊,須用 json_decode($json, false) 返回 stdClass 物件,或檢查原始 JSON 字串首字符 [ vs {。

最佳實踐清單

  1. API 契約優先: 定義 OpenAPI/Swagger,明確每個欄位類型(array vs object)。
  2. 列表永遠用 Array,實體永遠用 Object: 不要混淆語義。
  3. PHP 端: 刻意構建關聯陣列鍵,或使用 JSON_FORCE_OBJECT 配合文檔說明。
  4. 前端端: 統一用 response.json(),依契約斷言 Array.isArray(res.data.list)。
  5. 測試驗證: CI 中加入 JSON Schema 驗證,捕獲類型漂移。

總結

json_encode 將「連續零基整數鍵陣列」序列化為 JSON Array 是規格內行為,非 Bug。理解此規則,並在 API 設計層面統一語義(列表用 Array、實體用 Object、外層包裝 Object),即可徹底避免前後端類型不匹配。記住:契約大於慣例,顯式大於隱式。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved