
本文详解如何解析多层嵌套的 json(如 randomuser.me 接口返回的数据),安全提取深层字段,并通过预处理语句批量插入主表与关联子表,避免 sql 注入,同时正确处理外键关系。
本文详解如何解析多层嵌套的 json(如 randomuser.me 接口返回的数据),安全提取深层字段,并通过预处理语句批量插入主表与关联子表,避免 sql 注入,同时正确处理外键关系。
在 PHP 中处理嵌套 JSON 并持久化到数据库时,关键在于:结构化解构 + 安全绑定 + 关系映射。以下是一个完整、健壮、可复用的实践方案。
✅ 正确解析嵌套结构:逐层访问,防御性取值
原始 JSON 解析后得到的是多维关联数组(如 $data['results'][0])。直接访问 ['location']['street']['number'] 等深层键前,务必检查路径是否存在,防止 Notice: Undefined index 错误。推荐使用 ?? 空合并操作符或 isset() 封装:
$user = $data['results'][0]; // 示例单条 $title = $user['name']['title'] ?? ''; $firstname = $user['name']['first'] ?? ''; $lastname = $user['name']['last'] ?? ''; $street_num = $user['location']['street']['number'] ?? null; $street_name = $user['location']['street']['name'] ?? ''; $city = $user['location']['city'] ?? ''; // ... 其他字段同理
⚠️ 注意:
$user['email']中含 HTML<a></a>标签(由 Cloudflare Email Protection 生成),需用strip_tags()清洗:$email = strip_tags($user['email'] ?? '');
✅ 安全插入主表(users)与子表(user_pictures)
采用 MySQLi 面向对象风格 + 预处理语句,确保类型安全与防注入:
立即学习“PHP免费学习笔记(深入)”;
// 1. 准备主表插入语句(users)
$insert_user = $conn->prepare(
"INSERT INTO users (title, firstname, lastname, street_num, street_name, city, state, country, postcode,
latitude, longitude, tz_offset, tz_description, email, dob, age, reg_date, reg_age, phone, cell)
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)"
);
$insert_user->bind_param(
"sssissssffssssisiss",
$title, $firstname, $lastname, $street_num, $street_name, $city, $state, $country, $postcode,
$latitude, $longitude, $tz_offset, $tz_description, $email, $dob, $age, $reg_date, $reg_age, $phone, $cell
);
// 2. 准备子表插入语句(user_pictures)
$insert_pic = $conn->prepare("INSERT INTO user_pictures (user_id, size, url) VALUES (?, ?, ?)");
$insert_pic->bind_param("iss", $user_id, $size, $url); // 注意:user_id 是整型,用 'i'
// 3. 遍历 results 数组
foreach ($data['results'] as $user) {
// 提取并清洗字段(含空值防护)
$title = $user['name']['title'] ?? '';
$firstname = $user['name']['first'] ?? '';
$lastname = $user['name']['last'] ?? '';
$street_num = $user['location']['street']['number'] ?? null;
$street_name = $user['location']['street']['name'] ?? '';
$city = $user['location']['city'] ?? '';
$state = $user['location']['state'] ?? '';
$country = $user['location']['country'] ?? '';
$postcode = $user['location']['postcode'] ?? '';
$latitude = $user['location']['coordinates']['latitude'] ?? null;
$longitude = $user['location']['coordinates']['longitude'] ?? null;
$tz_offset = $user['location']['timezone']['offset'] ?? '';
$tz_description = $user['location']['timezone']['description'] ?? '';
$email = strip_tags($user['email'] ?? '');
$dob = $user['dob']['date'] ?? null;
$age = $user['dob']['age'] ?? null;
$reg_date = $user['registered']['date'] ?? null;
$reg_age = $user['registered']['age'] ?? null;
$phone = $user['phone'] ?? '';
$cell = $user['cell'] ?? '';
// 执行主表插入
if (!$insert_user->execute()) {
error_log("User insert failed: " . $insert_user->error);
continue; // 跳过当前用户,继续下一条
}
// 获取新插入用户的自增 ID
$user_id = $conn->insert_id;
// 插入多尺寸图片(picture 是关联数组:['large'=>'...', 'medium'=>'...'])
foreach ($user['picture'] as $size => $url) {
if (!empty($url)) {
$insert_pic->execute();
}
}
}✅ 关键注意事项与最佳实践
-
事务保障一致性:若需“全部成功或全部回滚”,将循环包裹在
mysqli_begin_transaction()/commit()/rollback()中; -
批量性能优化:对海量数据,可改用
INSERT ... VALUES (...), (...), (...)单语句多值插入,但需手动拼接并严格转义; -
字符集统一:确保数据库、连接、PHP 文件均为
UTF8MB4,避免中文/emoji 截断; -
错误处理不可省略:
$insert_user->execute()返回false时应记录日志并决定是否中断; -
内存友好:大 JSON 文件建议用
json_decode($json, true, 512, JSON_BIGINT_AS_STRING)防止整型溢出,并分批处理(如每 100 条提交一次)。
掌握这一模式,你不仅能处理 randomuser.me 的嵌套结构,也能灵活适配任意深度的 API 响应,构建稳定可靠的数据导入管道。



















