本文介绍一种简洁、可扩展的递归方法,替代传统多层 foreach 循环,高效提取 json api 返回的嵌套数组中同时包含 `value` 和 `id` 的 header 字段及其紧邻的 rows 数据。
当从财务类 API(如 QuickBooks Online 报表接口)获取结构灵活但语义一致的嵌套 JSON 数据时,开发者常面临“深度不可预知”的挑战:Rows → Row → Rows → Row → ... 可能嵌套 3 层、7 层甚至更深,而关键信息(如账户名称 "40000 Sales Income" 及其 ID "31")总出现在某一层的 Header.
ColData 中,其对应明细数据则紧随其后位于同级或下级的 Rows.Row.*.ColData 中。
硬编码多层 foreach 不仅易错、难维护,更无法应对动态报告结构(如“损益表”与“应收账款明细”深度差异大)。真正的解法是递归 + 精准路径守卫(Path Guarding)——即在每一层递归中,只关注当前节点是否满足业务提取条件,而非预设层级。
以下是一个健壮、可复用的递归函数实现:
function extractHeaderWithIdAndChildren(array $data): array {
$results = [];
// 安全遍历 Rows.Row(兼容缺失、空或非数组情况)
$rows = $data['Rows']['Row'] ?? [];
if (!is_array($rows)) {
return $results;
}
foreach ($rows as $row) {
// ✅ 条件一:存在 Header.ColData 且至少一个子项含 value + id
$headerColData = $row['Header']['ColData'] ?? [];
$hasValidHeader = false;
$validHeaderItem = null;
foreach ($headerColData as $col) {
if (isset($col['value'], $col['id']) && is_string($col['value']) && is_string($col['id'])) {
$hasValidHeader = true;
$validHeaderItem = $col;
break; // 取首个有效项(通常 Header 中仅首列含 id)
}
}
if ($hasValidHeader && $validHeaderItem) {
// 提取核心标识信息
$extracted = [
'value' => $validHeaderItem['value'],
'id' => $validHeaderItem['id']
];
// ✅ 条件二:提取“紧邻的后续 ColData”——优先取同级 Rows.Row 下的 ColData(即子行明细)
// 注意:根据示例结构,目标数据实际在 $row['Rows']['Row'][0]['ColData'](type: "Data")
$immediateColData = [];
$nestedRows = $row['Rows']['Row'] ?? [];
if (is_array($nestedRows) && !empty($nestedRows)) {
// 遍历子 Row,收集所有 type === "Data" 的 ColData(避免 Summary/Section)
foreach ($nestedRows as $childRow) {
if (isset($childRow['type']) && $childRow['type'] === 'Data' && isset($childRow['ColData'])) {
$immediateColData[] = $childRow['ColData'];
}
}
}
$results[] = [
'header' => $extracted,
'details' => $immediateColData // 多条明细,每条为 ColData 数组
];
} else {
// ❌ 当前行不匹配 → 递归进入其子结构(如深层嵌套的 Rows.Row)
// 注意:跳过 Summary/Section 等非数据节点,聚焦 Rows 分支
if (isset($row['Rows']) && is_array($row['Rows'])) {
$results = array_merge($results, extractHeaderWithIdAndChildren($row));
}
}
}
return $results;
}
// 使用示例:
$json = file_get_contents('api_response.json'); // 或来自 cURL 响应
$data = json_decode($json, true);
$extracted = extractHeaderWithIdAndChildren($data);
print_r($extracted);✅ 关键设计说明:
⚠️ 注意事项:
通过将“结构不确定性”转化为“逻辑确定性”,该方案让代码专注业务意图(找带 ID 的 Header,取它的数据子项),而非纠结于括号嵌套层数——这才是处理动态 API 数据的现代 PHP 实践。