如何使用jq筛选含嵌套键与列表的JSON对象并保留指定键
问题描述
假设我有如下JSON对象:
{ "a": 1, "b": 2, "c": [ { "d": 1, "e": 2 }, { "d": 2, "e": 3 } ] }
我希望仅提取一组可能为嵌套类型的键,路径格式例如.a和.c[].d,最终得到如下输出结果:
{ "a": 1, "c": [ { "d": 1 }, { "d": 2 } ] }
请问该如何实现上述需求?
实现方案
核心逻辑分为路径解析、递归过滤两个环节:
- 路径解析:将输入的
.c[].d类路径字符串拆分为分层访问标记,比如把.c[].d解析为['c', '*', 'd'],其中*代表需要遍历数组下的所有元素 - 递归过滤:从根节点开始遍历原始JSON结构,仅保留路径命中的字段:
- 遇到普通对象时,只保留当前路径层声明过的键,再向下递归处理对应子字段
- 遇到数组时,遍历所有数组元素,按数组对应的下一层路径规则递归处理每个元素
- 走到路径末尾时直接返回当前的基础类型值即可
以下是可直接运行的JavaScript实现:
function extractByPaths(source, paths) { // 预处理输入路径,拆分为分层访问段 const parsedPaths = paths.map(path => { const segments = [] path.replace(/^\./, '').split('.').forEach(seg => { if (seg.endsWith('[]')) { segments.push(seg.slice(0, -2)) segments.push('*') } else { segments.push(seg) } }) return segments }) // 递归提取核心方法 function recursiveExtract(currentVal, depth, validPaths) { const activePaths = validPaths.filter(p => p.length > depth) if (activePaths.length === 0) return undefined // 处理数组类型 if (Array.isArray(currentVal)) { return currentVal.map(item => { const arrayPaths = activePaths.filter(p => p[depth] === '*') return recursiveExtract(item, depth + 1, arrayPaths) }).filter(item => item !== undefined) } // 处理对象类型 if (typeof currentVal === 'object' && currentVal !== null) { const result = {} const keyRelatedPaths = {} activePaths.forEach(p => { const currentKey = p[depth] if (currentKey === '*') return if (!keyRelatedPaths[currentKey]) keyRelatedPaths[currentKey] = [] keyRelatedPaths[currentKey].push(p) }) Object.keys(keyRelatedPaths).forEach(key => { if (!(key in currentVal)) return const subResult = recursiveExtract(currentVal[key], depth + 1, keyRelatedPaths[key]) if (subResult !== undefined) result[key] = subResult }) return Object.keys(result).length > 0 ? result : undefined } // 处理基础类型值,命中路径则返回 const isHit = activePaths.some(p => p.length === depth + 1) return isHit ? currentVal : undefined } return recursiveExtract(source, 0, parsedPaths) } // 测试用例 const source = { "a": 1, "b": 2, "c": [ {"d": 1, "e": 2}, {"d": 2, "e": 3} ] } const result = extractByPaths(source, ['.a', '.c[].d']) console.log(JSON.stringify(result, null, 2))
上述实现支持多层嵌套路径,比如.a.b.c、.list[].subList[].id这类格式都可以正常处理,运行测试用例即可得到你预期的输出结果。
内容的提问来源于stack exchange,提问作者alon-k
相关产品推荐
相关产品推荐

