PHP JSON 解码失败?malformed UTF-8、extra data 一次性讲清楚

62 阅读1分钟

你是不是也遇到过这种报错👇

json_decode(): Malformed UTF-8 characters, possibly incorrectly encoded

或者:

json_decode(): Syntax error, unexpected 'extra data'

接口明明返回了内容,
就是解析不了

先说结论一句话:

90% 的 JSON 解码失败,不是 JSON 写错,是“编码 / 返回内容不干净”。

下面我们一步一步来。


一、最常见的 2 种报错,到底是什么意思?

malformed UTF-8 characters

直译:字符串里有“非法 UTF-8 字符”

常见原因只有几个:

  • 接口返回 GBK / GB2312
  • 数据里混了中文 + 非 UTF-8
  • 字符串前后带 BOM
  • 从文件 / 数据库读出来就已经是脏数据

extra data

意思是:
👉 JSON 是对的,但后面还多了东西

比如接口实际返回的是:

{"code":0,"msg":"ok"}<br />

或者:

{"data":123}
{"debug":"xxx"}

JSON 只能解析一份干净数据
后面多一个字节都不行。


二、第一步:永远先把“原始返回”打出来

新手最容易犯的错是:
直接 json_decode,不看原始内容

你一定要先这样:

$response = curl_exec($ch);
var_dump($response);
exit;

你会惊讶地发现👇

  • 看似 JSON,其实前面有空格
  • 后面多了一行 HTML 报错
  • 中文全是乱码

三、malformed UTF-8 的 6 种真实原因

1️⃣ 接口不是 UTF-8

国内老接口常见:

  • GBK
  • GB2312

解决:

$response = mb_convert_encoding($response, 'UTF-8', 'GBK');

2️⃣ UTF-8 + BOM(隐形杀手)

字符串开头有三个字节:

EF BB BF

处理方式:

$response = preg_replace('/^\xEF\xBB\xBF/', '', $response);

3️⃣ 数据库本身不是 UTF-8

  • 表是 utf8
  • 字段是 latin1

👉 读出来就炸。


4️⃣ 文件内容编码不对

file_get_contents() 读的文件不是 UTF-8,
直接解码必报错。


5️⃣ 字符串里混了不可见字符

比如:

  • 控制符
  • 非法二进制

可以先做清洗:

$response = iconv('UTF-8', 'UTF-8//IGNORE', $response);

6️⃣ PHP 源文件本身有 BOM

你以为是接口问题,
其实是 PHP 文件保存格式错了


四、extra data 的 5 种高频场景

1️⃣ 接口输出了 HTML

  • Notice
  • Warning
  • Debug echo

✔ 关闭错误输出,查日志。


2️⃣ 后端多 echo 了一句

echo json_encode($data);
echo "success";

👉 必炸。


3️⃣ 多次 json_encode

返回了两段 JSON。


4️⃣ gzip 没解压

看起来是乱码,其实是压缩内容。

curl_setopt($ch, CURLOPT_ENCODING, '');

5️⃣ 接口前后有空格 / 换行

有些严格环境也会报错。


五、万能 JSON 解码安全写法(直接用)

$response = trim($response);

// 去 BOM
$response = preg_replace('/^\xEF\xBB\xBF/', '', $response);

// 强制转 UTF-8
$response = mb_convert_encoding($response, 'UTF-8', 'UTF-8,GBK,GB2312,ISO-8859-1');

$data = json_decode($response, true);

if (json_last_error() !== JSON_ERROR_NONE) {
    echo json_last_error_msg();
    var_dump($response);
    exit;
}

👉 这段代码能解决 80% 的 JSON 解码失败。


六、快速自检清单(收藏级)

你可以按顺序问自己:

  • 原始返回我看了吗?
  • 编码是不是 UTF-8?
  • 有没有 BOM?
  • 有没有多余输出?
  • gzip 解了吗?
  • 是不是接口本身在报错?