
本文介绍一种轻量、无依赖的路径式对象字段提取方案,支持嵌套属性、数组索引(如 [0]、[:last])、通配符(如 book.heroes.name)及包含/排除混合策略,适用于 API 层动态字段裁剪场景。
本文介绍一种轻量、无依赖的路径式对象字段提取方案,支持嵌套属性、数组索引(如 `[0]`、`[:last]`)、通配符(如 `book.heroes.name`)及包含/排除混合策略,适用于 api 层动态字段裁剪场景。
在构建面向前端的灵活 API 时,常需根据客户端请求动态裁剪响应体——既保留标准字段(如 user.name, book.title),又支持用户自定义的任意嵌套结构(如 user.custom.preferences.theme)。GraphQL 虽强大,但强依赖预定义 Schema,难以应对 custom 字段完全开放、结构不可知的场景。此时,一套基于路径表达式的运行时提取机制更为实用。
以下是一个生产就绪的核心实现,支持 include/exclude 混合语义、数组切片与通配访问:
const extractData = (data, filters) => {
// 解析过滤规则:true → 包含路径;false → 排除路径
const [pathsToInclude, pathsToExclude] = Object.entries(filters).reduce(
(acc, [keyPath, include]) => {
const pathParts = parsePath(keyPath);
acc[include ? 0 : 1].push(pathParts);
return acc;
},
[[], []]
);
// 初始化结果:若存在 include 规则,则从空对象开始;否则深拷贝原始数据
const result = pathsToInclude.length > 0 ? {} : deepClone(data);
// 执行包含逻辑:逐路径递归挂载目标值
pathsToInclude.forEach((path) => {
let current = path.shift();
let obj = result;
let dataObj = data;
while (current !== undefined && obj !== null && dataObj !== undefined) {
if (path.length === 0) {
// 叶子节点:直接赋值(支持数组索引与通配)
obj[current] = resolveValueAt(dataObj, current, path);
} else {
// 中间节点:确保目标位置为对象
obj[current] = obj[current] ?? {};
dataObj = dataObj?.[current];
}
obj = obj[current];
current = path.shift();
}
});
// 执行排除逻辑:定位父级后删除指定键
pathsToExclude.forEach((path) => {
const keyToDelete = path.pop();
let current = path.shift();
let obj = result;
while (obj && current !== undefined) {
if (path.length === 0) break;
obj = obj[current];
current = path.shift();
}
if (obj && obj.hasOwnProperty(keyToDelete)) {
delete obj[keyToDelete];
}
});
return result;
};
// 解析路径字符串(支持 . 和 [] 语法)
const parsePath = (str) => {
return str
.replace(/\[(\d+|:last|:all)\]/g, '.$1') // 将 [0] → .0, [:last] → .:last
.split('.')
.filter(Boolean);
};
// 根据路径片段解析值(处理数组索引与通配)
const resolveValueAt = (obj, key, remainingPath) => {
if (!obj || typeof obj !== 'object') return undefined;
// 处理数组索引:0, :last, :all
if (Array.isArray(obj) && /^[0-9]+|:last|:all$/.test(key)) {
if (key === ':last' && obj.length > 0) {
return resolveDeep(obj[obj.length - 1], remainingPath);
}
if (key === ':all') {
return obj.map(item => resolveDeep(item, remainingPath));
}
const idx = parseInt(key, 10);
if (!isNaN(idx) && idx >= 0 && idx {
if (path.length === 0 || value === undefined) return value;
if (Array.isArray(value)) {
return value.map(item => resolveDeep(item, path));
}
if (typeof value === 'object' && value !== null) {
const [first, ...rest] = path;
return resolveDeep(value[first], rest);
}
return undefined;
};
// 简易深克隆(仅处理普通对象/数组,不含 Date/RegExp 等)
const deepClone = (obj) => {
if (obj === null || typeof obj !== 'object') return obj;
if (Array.isArray(obj)) return obj.map(deepClone);
const cloned = {};
for (const key in obj) {
if (Object.prototype.hasOwnProperty.call(obj, key)) {
cloned[key] = deepClone(obj[key]);
}
}
return cloned;
};
使用示例:
当用户要求为 Java 代码添加注释、修改注释、审查注释、整理注释格式,或要求按照代码注释规范处理 Java 文件时,使用本技能。本技能用于统一生成和维护 Java 类注释模板、按 Git 对比 release 判断版本号、补充每行代码上方中文块注释、调整注释空行规则,并保持 Java 文件 UTF-8 无 BOM 编码。典型触发场景包括:补充类注释、更新 @version、检查 @author 来源、整理行尾注释、把注释放到代码上一行、检查注释是否符合规范、构建前检查 Java 注释风格。
const data = {
user: { age: 25, weight: 170, name: "Joe" },
book: {
title: "Dune",
description: "Space opera",
location: { planet: "Arakis", population: 100000 },
heroes: [
{ name: "Duke", age: 43 },
{ name: "Paul", age: 16 }
]
}
};
// ✅ 提取指定字段
console.log(extractData(data, { "user.name": true, "book.location.planet": true }));
// → { user: { name: "Joe" }, book: { location: { planet: "Arakis" } } }
// ✅ 排除子树
console.log(extractData(data, { "book": true, "book.heroes": false }));
// → { book: { title: "Dune", description: "Space opera", location: { ... } } }
// ✅ 数组操作
console.log(extractData(data, { "book.heroes[0].name": true }));
// → { book: { heroes: [{ name: "Duke" }] } }
console.log(extractData(data, { "book.heroes[:last].name": true }));
// → { book: { heroes: [{ name: "Paul" }] } }
console.log(extractData(data, { "book.heroes.name": true }));
// → { book: { heroes: ["Duke", "Paul"] } }
注意事项:
- 本方案不依赖外部库,可直接集成于 Express/Koa 中间件,对 req.query.fields 或 req.body.filters 进行解析;
- parsePath 支持扩展正则以兼容更多语法(如 [1:3] 切片);
- deepClone 为简化版,生产环境建议替换为 structuredClone(Node.js ≥17.0)或 lodash.cloneDeep;
- 安全起见,应在服务端对用户传入的路径做白名单校验(如禁止 __proto__、constructor 等敏感键);
- 高频调用场景下,可缓存 parsePath 结果或预编译路径解析器提升性能。
该方案平衡了灵活性与可控性,让 API 响应真正“按需生成”,是 GraphQL 轻量化落地的理想补充。
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南










