json_decode() 返回 null 通常因 json 字符串不合法或编码错误,应先用 json_last_error() 定位问题,检查字符串完整性、编码一致性,并注意解码后默认为对象而非数组,建议统一使用 json_decode($json, true) 返回关联数组以避免类型误用。

json_decode() 返回 null 怎么办
PHP 读不到 JSON 数据,八成是 json_decode() 返回了 null,而不是你想象中的数组或对象。这不是代码写错了,而是 JSON 字符串本身不合法,或者编码不对。
常见错误现象:var_dump(json_decode($json)) 输出 NULL,但字符串看着“明明有内容”。这时候别急着循环,先检查源头:
- 用
json_last_error()和json_last_error_msg()看具体错在哪,比如JSON_ERROR_UTF8就是编码问题 - 确认输入字符串是完整、未被截断的 —— 特别是来自
file_get_contents()或 cURL 响应时,可能因超时/网络中断导致半截 JSON - 中文字段没加
JSON_UNESCAPED_UNICODE不会报错,但若原始 JSON 是 GBK 编码而 PHP 当 UTF-8 解,json_decode()就直接返回null
遍历 json_decode() 后的数组还是对象
默认情况下 json_decode() 返回对象(stdClass),不是数组 —— 所以用 foreach ($data as $k => $v) 会报 Invalid argument supplied for foreach(),除非你明确传了第二个参数 true。
使用场景取决于你后续怎么处理数据:想用 $data['name'] 就用数组;习惯 $data->name 就用对象(更接近 JS 风格)。
立即学习“PHP免费学习笔记(深入)”;
实操建议:
- 统一用
json_decode($json, true),避免后期反复判断类型,也省得foreach前还要(array)强转 - 如果 JSON 结构固定且嵌套深(比如
{"data":{"list":[{"id":1}]}}),解出来是多层关联数组,直接用键名链式访问即可,不用递归遍历 - 对象模式下想遍历属性,必须用
get_object_vars()包一层才能foreach,否则会跳过私有/受保护属性(虽然 JSON 没这概念,但 PHP 对象有)
foreach 遍历多层嵌套 JSON 数组卡死或漏数据
不是 PHP 卡了,是你的 JSON 里混了 null、空数组或非数组值,但你在 foreach 前没做防御性检查。
典型错误现象:循环到某一层突然报 Invalid argument supplied for foreach(),或者只输出前几条就停了。
原因和对策:
-
foreach ($data['items'] as $item)—— 如果$data['items']实际是null或字符串,就会崩。加一层is_array($data['items'])判断再进循环 - 嵌套结构中某条记录字段缺失(比如有的有
"tags",有的没有),直接foreach ($item['tags'] as ...)就挂。改用isset($item['tags']) && is_array($item['tags']) - 用
array_key_exists()而不是isset()来判断键是否存在 —— 因为isset()对null值返回false,但键其实是存在的
json_decode() 性能和大文件注意事项
单次解析几 MB 的 JSON 文件,json_decode() 本身不慢,但内存暴涨是真问题 —— 它会把整个 JSON 映射成 PHP 的数组/对象结构,内存占用通常是原始字符串的 3–5 倍。
如果你只是想逐条读取日志类 JSON 行(每行一个 JSON 对象),别用 json_decode(file_get_contents()) 全读进内存:
- 用
file()或fopen()+fgets()逐行读,每行单独json_decode() - 确认 JSON 是严格的一行一对象(NDJSON 格式),不是缩进格式,否则
fgets()会读不全 - 大数组遍历时避免在循环体内反复调用
count()—— 改成$len = count($arr); for ($i = 0; $i ,尤其在旧版 PHP 中差异明显
真正容易被忽略的是:JSON 中带大量重复键名(比如 1000 条记录都有 "created_at"),PHP 数组会为每个键单独分配哈希桶,比对象模式更吃内存。这时候该考虑用 json_decode($json, false) 返回对象,并配合 foreach (get_object_vars($obj) as ...) 控制访问粒度。











