JSON 编解码
JSON(JavaScript Object Notation)是现代 Web 开发中最常用的数据交换格式。PHP 提供了 json_encode() 和 json_decode() 两个核心函数用于 JSON 的编码和解码,PHP 8.2+ 还新增了 JsonSerializable 接口的改进。本节将全面讲解 PHP 的 JSON 处理。
基础概念
JSON 格式
JSON 支持以下数据类型:
| JSON 类型 | PHP 类型 | 示例 |
|---|---|---|
| object | array(关联数组) | {"name":"张三"} |
| array | array(索引数组) | [1,2,3] |
| string | string | "hello" |
| number (int) | int | 42 |
| number (float) | float | 3.14 |
| boolean | bool | true / false |
| null | null | null |
PHP 扩展
JSON 扩展是 PHP 默认启用的核心扩展(ext-json),不需要额外安装。
json_encode 编码
基本用法
php
<?php
declare(strict_types=1);
// 编码数组
$data = [
'name' => '张三',
'age' => 30,
'email' => 'zhangsan@example.com',
'active' => true,
'scores' => [95, 88, 92, 78],
];
$json = json_encode($data);
// {"name":"张三","age":30,"email":"zhangsan@example.com","active":true,"scores":[95,88,92,78]}
// 格式化输出(美化)
$json = json_encode($data, JSON_PRETTY_PRINT);编码选项
php
<?php
declare(strict_types=1);
$data = [
'name' => '张三',
'content' => '<strong>HTML 内容</strong>',
'path' => 'C:\\Users\\test',
'unicode' => '中文 Japanese 한국어',
'empty' => '',
'number' => 123456.789,
'bigNum' => 999999999999999999,
];
// JSON_UNESCAPED_UNICODE - 不转义 Unicode 字符
echo json_encode($data, JSON_UNESCAPED_UNICODE);
// {"name":"张三","content":"<strong>HTML 内容</strong>"...}
// JSON_UNESCAPED_SLASHES - 不转义斜杠
echo json_encode($data, JSON_UNESCAPED_SLASHES);
// JSON_PRETTY_PRINT - 格式化输出
echo json_encode($data, JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE);
// JSON_FORCE_OBJECT - 强制将数组编码为对象
echo json_encode([1, 2, 3], JSON_FORCE_OBJECT);
// {"0":1,"1":2,"2":3}
// JSON_HEX_TAG / JSON_HEX_AMP / JSON_HEX_APOS / JSON_HEX_QUOT
// 将特殊字符编码为十六进制
echo json_encode($data, JSON_HEX_TAG | JSON_HEX_AMP);
// JSON_NUMERIC_CHECK - 将数字字符串转为数字
echo json_encode(['id' => '123', 'price' => '99.99'], JSON_NUMERIC_CHECK);
// JSON_PRESERVE_ZERO_FRACTION - 保留浮点数的零小数
echo json_encode(10.0, JSON_PRESERVE_ZERO_FRACTION);
// 10.0 (而不是 10)
// JSON_THROW_ON_ERROR - 错误时抛出 JsonException(PHP 7.3+)
try {
$json = json_encode("\xB1\x22", JSON_THROW_ON_ERROR);
} catch (JsonException $e) {
echo "JSON 编码错误: " . $e->getMessage();
}
// JSON_INVALID_UTF8_SUBSTITUTE (PHP 7.2+)
// 无效 UTF-8 替换为 Unicode 替换字符
echo json_encode("\xFF", JSON_INVALID_UTF8_SUBSTITUTE);
// JSON_INVALID_UTF8_IGNORE (PHP 7.2+)
// 忽略无效 UTF-8 字符
echo json_encode("\xFF text", JSON_INVALID_UTF8_IGNORE);
// JSON_PARTIAL_OUTPUT_ON_ERROR - 部分输出(即使出错也返回部分结果)
echo json_encode(["depth" => "too"."\x80"."\ndeep"], JSON_PARTIAL_OUTPUT_ON_ERROR);
// JSON_UNESCAPED_LINE_TERMINATORS / JSON_UNESCAPED_UNICODE (PHP 7.1+)
// 常用组合
$prettyFlags = JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES;
echo json_encode($data, $prettyFlags);编码深度限制
php
<?php
declare(strict_types=1);
// 深层嵌套数据
$deepData = [];
$current = &$deepData;
for ($i = 0; $i < 520; $i++) {
$current['level'] = $i;
$current['child'] = [];
$current = &$current['child'];
}
// PHP 8.1+ 默认深度限制为 512
// json_encode($deepData); // 返回 false
// 设置更大的深度(PHP 8.1+ 通过 json_encode 的 options)
$json = json_encode($deepData, 0, 1024); // depth 参数json_decode 解码
基本用法
php
<?php
declare(strict_types=1);
$json = '{"name":"张三","age":30,"active":true,"scores":[95,88,92]}';
// 解码为关联数组
$data = json_decode($json, true);
// ['name' => '张三', 'age' => 30, 'active' => true, 'scores' => [95, 88, 92]]
// 解码为对象(默认)
$obj = json_decode($json);
// stdClass { name: '张三', age: 30, active: true, scores: [95, 88, 92] }
// 访问对象属性
echo $obj->name; // 张三
echo $obj->scores[0]; // 95解码选项
php
<?php
declare(strict_types=1);
$json = '{"name":"张三","empty":"","null_value":null,"big_number":12345678901234567890}';
// JSON_BIGINT_AS_STRING - 大整数转为字符串
$data = json_decode($json, true, 512, JSON_BIGINT_AS_STRING);
// 'big_number' => '12345678901234567890'
// JSON_OBJECT_AS_ARRAY - 强制对象为数组
$data = json_decode($json, false, 512, JSON_OBJECT_AS_ARRAY);
// JSON_THROW_ON_ERROR - 错误时抛出异常
try {
$data = json_decode('{"invalid": }', true, 512, JSON_THROW_ON_ERROR);
} catch (JsonException $e) {
echo "JSON 解码错误: " . $e->getMessage();
}
// JSON_INVALID_UTF8_SUBSTITUTE / JSON_INVALID_UTF8_IGNORE (PHP 7.2+)
// 处理无效 UTF-8
$data = json_decode($jsonWithBadUtf8, true, 512, JSON_INVALID_UTF8_SUBSTITUTE);错误处理
php
<?php
declare(strict_types=1);
// 方式一:JSON_THROW_ON_ERROR
try {
$data = json_decode('{invalid}', true, 512, JSON_THROW_ON_ERROR);
} catch (JsonException $e) {
echo "错误: " . $e->getMessage() . PHP_EOL;
}
// 方式二:手动检查
$json = '{invalid json}';
$data = json_decode($json);
if ($data === null && json_last_error() !== JSON_ERROR_NONE) {
echo "错误: " . json_last_error_msg() . PHP_EOL;
}
// json_last_error() 返回错误码
// json_last_error_msg() 返回错误消息(PHP 5.5+)
// 错误码常量:
// JSON_ERROR_NONE - 无错误
// JSON_ERROR_DEPTH - 超过最大深度
// JSON_ERROR_STATE_MISMATCH - 状态不匹配
// JSON_ERROR_CTRL_CHAR - 遇到意外的控制字符
// JSON_ERROR_SYNTAX - 语法错误
// JSON_ERROR_UTF8 - UTF-8 编码错误
// JSON_ERROR_RECURSION - 递归引用
// JSON_ERROR_INF_OR_NAN - INF 或 NaN
// JSON_ERROR_UNSUPPORTED_TYPE - 不支持的类型
// JSON_ERROR_INVALID_PROPERTY_NAME (PHP 7.0+)
// JSON_ERROR_NON_BACKED_ENUM_TYPE (PHP 8.1+)JsonSerializable 接口
基本实现
php
<?php
declare(strict_types=1);
class User implements JsonSerializable
{
public function __construct(
private readonly int $id,
private readonly string $name,
private readonly string $email,
private readonly \DateTime $createdAt,
private readonly array $roles = []
) {}
public function jsonSerialize(): mixed
{
return [
'id' => $this->id,
'name' => $this->name,
'email' => $this->email,
'created_at' => $this->createdAt->format('c'),
'roles' => $this->roles,
];
}
}
$user = new User(
1,
'张三',
'zhangsan@example.com',
new \DateTime('2024-01-15'),
['admin', 'editor']
);
echo json_encode($user, JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE);
// {
// "id": 1,
// "name": "张三",
// "email": "zhangsan@example.com",
// "created_at": "2024-01-15T00:00:00+00:00",
// "roles": ["admin", "editor"]
// }JsonSerializable 嵌套对象
php
<?php
declare(strict_types=1);
class Address implements JsonSerializable
{
public function __construct(
private readonly string $street,
private readonly string $city,
private readonly string $zipCode
) {}
public function jsonSerialize(): mixed
{
return [
'street' => $this->street,
'city' => $this->city,
'zipCode' => $this->zipCode,
];
}
}
class Order implements JsonSerializable
{
public function __construct(
private readonly string $orderId,
private readonly User $user,
private readonly Address $address,
private readonly array $items = []
) {}
public function jsonSerialize(): mixed
{
return [
'order_id' => $this->orderId,
'user' => $this->user, // 自动调用 User::jsonSerialize()
'address' => $this->address, // 自动调用 Address::jsonSerialize()
'items' => $this->items,
'total' => array_sum(array_column($this->items, 'price')),
];
}
}PHP 8.1+ 枚举与 JSON
php
<?php
declare(strict_types=1);
// PHP 8.1+ Backed Enum 自动支持 JSON 编解码
enum Status: string implements JsonSerializable
{
case Active = 'active';
case Inactive = 'inactive';
case Pending = 'pending';
public function jsonSerialize(): string
{
return $this->value;
}
}
enum Color: int
{
case Red = 1;
case Green = 2;
case Blue = 3;
}
// Backed Enum 可以直接 json_encode
echo json_encode(Status::Active); // "active"
echo json_encode(Color::Red); // 1
// 解码需要手动映射
$json = '"active"';
$statusValue = json_decode($json, true);
$status = Status::tryFrom($statusValue);实战示例
JSON API 响应
php
<?php
declare(strict_types=1);
/**
* JSON API 响应构造器
*/
class JsonResponse
{
public static function success(
mixed $data = null,
string $message = 'success',
int $code = 0
): string {
return json_encode([
'code' => $code,
'message' => $message,
'data' => $data,
], JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES);
}
public static function error(
string $message,
int $code = 500,
mixed $data = null
): string {
return json_encode([
'code' => $code,
'message' => $message,
'data' => $data,
], JSON_UNESCAPED_UNICODE);
}
public static function paginated(
array $items,
int $total,
int $page,
int $perPage
): string {
return json_encode([
'code' => 0,
'data' => $items,
'pagination' => [
'total' => $total,
'page' => $page,
'per_page' => $perPage,
'last_page' => (int) ceil($total / $perPage),
],
], JSON_UNESCAPED_UNICODE);
}
}
// 使用示例
echo JsonResponse::success(['user' => ['id' => 1, 'name' => '张三']]);
echo JsonResponse::error('参数无效', 400);
echo JsonResponse::paginated($items, 100, 1, 10);JSON 文件读写
php
<?php
declare(strict_types=1);
/**
* JSON 文件操作
*/
class JsonFile
{
public static function read(string $path, bool $associative = true): mixed
{
$content = file_get_contents($path);
if ($content === false) {
throw new RuntimeException("无法读取文件: {$path}");
}
$data = json_decode($content, $associative, 512, JSON_THROW_ON_ERROR);
return $data;
}
public static function write(string $path, mixed $data, bool $pretty = true): void
{
$flags = JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES;
if ($pretty) {
$flags |= JSON_PRETTY_PRINT;
}
$json = json_encode($data, $flags);
if ($json === false) {
throw new RuntimeException("JSON 编码失败");
}
$result = file_put_contents($path, $json);
if ($result === false) {
throw new RuntimeException("无法写入文件: {$path}");
}
}
public static function merge(string $path, array $data): void
{
$existing = self::read($path);
$merged = array_merge($existing, $data);
self::write($path, $merged);
}
}
// 使用示例
// JsonFile::write('config.json', ['debug' => true, 'cache' => false]);
// $config = JsonFile::read('config.json');
// JsonFile::merge('config.json', ['timezone' => 'Asia/Shanghai']);注意事项
编码陷阱
php
<?php
declare(strict_types=1);
// 1. 索引数组和关联数组的区别
$indexed = ['a', 'b', 'c']; // JSON 数组
$assoc = ['x' => 'a', 'y' => 'b']; // JSON 对象
$mixed = [0 => 'a', 'x' => 'b']; // JSON 对象(包含非连续/非数字键)
// 2. json_decode 返回 null
$json = 'null';
$data = json_decode($json, true); // null
// 无法区分 "null" 和解码失败,需要检查 json_last_error()
if ($data === null && json_last_error() !== JSON_ERROR_NONE) {
// 解码失败
}
// 3. 浮点精度
echo json_encode(0.1 + 0.2); // 0.30000000000000004
echo json_encode(round(0.1 + 0.2, 2)); // 0.3
// 4. 递归引用
$a = [];
$a[] = &$a;
json_encode($a); // JSON_ERROR_RECURSION最佳实践
- 始终使用
JSON_THROW_ON_ERROR:避免静默失败 - 使用
JSON_UNESCAPED_UNICODE:中文等 Unicode 字符直接输出 - 实现
JsonSerializable:自定义对象的 JSON 序列化 - 处理大整数:使用
JSON_BIGINT_AS_STRING防止精度丢失 - 验证 JSON 输入:使用
json_validate()(PHP 8.3+)
下一节
继续学习:json_validate