在接口对接或数据清洗场景中,我们经常拿到一份JSON,里面的某些字段是缩写或数字编码,需要按照约定的键值对映射成更有意义的名称或值。Node.js作为服务端常用的JavaScript运行环境,处理这类转换非常方便,既可以用原生语法完成,也能借助社区模块。下面通过具体示例说明几种可靠的做法。

一、基础场景:扁平对象的字段替换
假设有一段用户数据,其中的role字段用数字表示,我们需要把它替换成对应的字符串说明。最直观的方式是定义一个映射表,然后遍历对象属性进行赋值。这种做法不依赖任何第三方包,适合字段较少且结构扁平的情况。
下面是一段有问题的写法:直接用全局字符串替换会把所有出现的数字都改掉,可能误伤id等字段。正确思路是针对特定键做判断。示例代码如下:
const rawData = {
id: 1001,
name: '张三',
role: 2
};
// 键值对映射表
const roleMap = {
1: '管理员',
2: '编辑',
3: '访客'
};
// 只替换role字段
function replaceField(obj, key, map) {
if (obj.hasOwnProperty(key) && map[obj[key]] !== undefined) {
obj[key] = map[obj[key]];
}
return obj;
}
const result = replaceField(rawData, 'role', roleMap);
console.log(result);
// 输出: { id: 1001, name: '张三', role: '编辑' }
上面的函数通过hasOwnProperty确认字段存在,并从映射表取出新值。如果原始数据是一个数组,只需要用Array.map把每个元素都过一遍函数即可。这种写法清晰且容易测试,不会影响到其他字段。
不过当字段名也需要变更时,比如把role改成roleName,就不能原地修改,而要返回新对象。可以用展开运算符生成副本,避免改动原数据导致副作用。
function renameAndReplace(obj, oldKey, newKey, map) {
const newObj = { ...obj };
if (newObj[oldKey] !== undefined) {
const val = map[newObj[oldKey]];
delete newObj[oldKey];
if (val !== undefined) {
newObj[newKey] = val;
} else {
newObj[newKey] = newObj[oldKey];
}
}
return newObj;
}
二、深层嵌套JSON的递归替换
真实业务里的JSON往往有多层嵌套,例如订单里包含用户、商品列表,商品里又有分类编码。此时扁平遍历不够用,需要写递归函数,在每一层检查是否存在目标键。递归能确保所有深度的匹配字段都被处理,但要注意避免循环引用造成死循环。
下面的例子演示如何根据键值对把任意层级中的status码替换成文字,同时保留结构:
const statusMap = {
0: '待支付',
1: '已支付',
2: '已发货',
3: '已完成'
};
function deepReplace(node, map) {
if (Array.isArray(node)) {
return node.map(item => deepReplace(item, map));
} else if (node !== null && typeof node === 'object') {
const out = {};
for (const k of Object.keys(node)) {
if (k === 'status' && map[node[k]] !== undefined) {
out[k] = map[node[k]];
} else {
out[k] = deepReplace(node[k], map);
}
}
return out;
}
return node;
}
const order = {
id: 'A001',
user: { name: '李四', status: 1 },
items: [
{ sku: 'x', status: 2 },
{ sku: 'y', status: 0 }
]
};
console.log(deepReplace(order, statusMap));
该函数对数组和对象分别处理:遇到数组就映射每个元素,遇到对象就遍历键,如果键名是status且映射表中有对应值就替换,否则继续向下递归。这样无论status出现在哪一层都能被统一转换。
如果JSON体积很大,递归会带来一定调用栈开销。可以考虑用显式栈或队列做迭代遍历,但在绝大多数后台接口转换中,递归的可读性和实现成本优势更明显。
三、使用第三方库简化代码
当映射规则复杂、或需要批量重命名字段时,社区里的map-obj这类小工具能减少样板代码。它支持对对象的每个键值对执行回调,并返回新对象,默认深遍历。下面是用map-obj做键值替换的示例:
const mapObj = require('map-obj');
const input = { type: 1, nested: { type: 2 } };
const typeMap = { 1: 'red', 2: 'blue' };
const output = mapObj(input, (key, value) => {
if (key === 'type' && typeMap[value]) {
return [key, typeMap[value]];
}
return [key, value];
}, { deep: true });
console.log(output);
使用库的好处是边界情况有人维护,例如处理Buffer、Date等类型时不易出错。但引入依赖会增加安装体积,在轻量脚本或冷启动敏感的函数计算里,原生递归往往更合适。
总结来说,根据键值对替换JSON字段,核心就是准备映射表加遍历逻辑。扁平数据用手写函数最直接,嵌套数据用递归或map-obj,按项目规模和可维护性做选择即可。