
挑战:JSON对象与类属性的不匹配
在前端开发中,我们经常会从后端api或其他数据源接收到json格式的数据。这些数据的键名可能并不总是与我们前端定义的javascript类属性名称完全一致。例如,我们可能收到以下结构的库存数据:
var inputJson = [{
"HS": " ",
"__EMPTY": 1,
"__EMPTY_1": " Pcs."
},
{
"HS": " RAS222PCAIBA",
"__EMPTY": 1,
"__EMPTY_1": " Pcs."
},
{
"HS": " SRK20CSS-S6/A1.6T",
"__EMPTY": -4,
"__EMPTY_1": " Pcs."
}];而我们希望将其映射到一个结构化的Stocks类实例中,该类定义如下:
class Stocks {
model; // 对应 inputJson 中的 "HS"
quantity; // 对应 inputJson 中的 "__EMPTY"
type; // 对应 inputJson 中的 "__EMPTY_1"
constructor(data) {
// 尝试直接赋值,但如果data的键名与类属性不匹配,会导致问题
Object.assign(this, data);
}
}如果直接尝试将inputJson中的每个对象传递给Stocks类的构造函数,例如:
var completeStock = [];
// 使用 $.each 迭代,也可以是 Array.prototype.forEach
$.each(inputJson, function(index, value) {
completeStock.push(new Stocks(value));
});这种方法会导致Stocks实例上出现HS、__EMPTY、__EMPTY_1等属性,而不是我们期望的model、quantity、type。这是因为Object.assign(this, data)会直接复制data对象的所有可枚举属性到this上,并不会进行键名转换。
解决方案:利用ES6解构赋值进行属性重命名
为了解决键名不匹配的问题,我们可以利用ES6的解构赋值(Destructuring Assignment)结合属性重命名(Property Renaming)功能。这个特性允许我们在从对象中提取属性值的同时,为其指定新的变量名。
立即学习“Java免费学习笔记(深入)”;
其基本语法是:const { originalKey: newKeyName } = sourceObject;
应用到我们的场景中,我们可以在创建Stocks实例之前,先从原始JSON对象中提取并重命名所需的属性:
// 假设 inputJson 和 Stocks 类已定义
var completeStock = [];
$.each(inputJson, function(index, value) {
// 使用解构赋值,将原始键名映射到目标属性名
const { HS: model, __EMPTY: quantity, __EMPTY_1: type } = value;
// 使用重命名后的属性创建新的 Stocks 实例
completeStock.push(new Stocks({ model, quantity, type }));
});
console.log(completeStock);这段代码的关键在于 { HS: model, __EMPTY: quantity, __EMPTY_1: type } = value;。它做了以下几件事:
- 从value对象中查找名为HS的属性,并将其值赋给一个名为model的新变量。
- 类似地,将__EMPTY的值赋给quantity变量。
- 将__EMPTY_1的值赋给type变量。
之后,new Stocks({ model, quantity, type })会使用这些已经正确命名的变量来构造一个对象 { model: '...', quantity: ..., type: '...' },再通过Object.assign正确地赋值给Stocks实例的相应属性。
完整示例与代码解析
下面是包含原始数据、类定义和解决方案的完整代码示例:
// 原始JSON数据数组
const inputJson = [{
"HS": " ",
"__EMPTY": 1,
"__EMPTY_1": " Pcs."
},
{
"HS": " RAS222PCAIBA",
"__EMPTY": 1,
"__EMPTY_1": " Pcs."
},
{
"HS": " SRK20CSS-S6/A1.6T",
"__EMPTY": -4,
"__EMPTY_1": " Pcs."
}];
// 目标类定义
class Stocks {
model;
quantity;
type;
constructor(data) {
// 这里的 Object.assign 假设传入的 data 对象的键名已经与类属性匹配
Object.assign(this, data);
}
}
// 存储映射后的 Stocks 实例的数组
const completeStock = [];
// 使用 $.each 迭代 inputJson 数组
// 注意:在现代JavaScript中,更推荐使用 Array.prototype.map
$.each(inputJson, function(index, value) {
// 核心步骤:使用解构赋值和属性重命名,将原始JSON键映射到类属性名
const {
HS: model, // 将 value.HS 的值赋给 model 变量
__EMPTY: quantity, // 将 value.__EMPTY 的值赋给 quantity 变量
__EMPTY_1: type // 将 value.__EMPTY_1 的值赋给 type 变量
} = value;
// 创建一个新的 Stocks 实例,并传入一个包含正确属性名的对象
completeStock.push(new Stocks({ model, quantity, type }));
});
console.log(completeStock);
/*
预期输出示例:
[
Stocks { model: ' ', quantity: 1, type: ' Pcs.' },
Stocks { model: ' RAS222PCAIBA', quantity: 1, type: ' Pcs.' },
Stocks { model: ' SRK20CSS-S6/A1.6T', quantity: -4, type: ' Pcs.' }
]
*/代码解析:
- inputJson: 原始数据源,一个包含多个具有非标准键名对象的数组。
- Stocks 类: 定义了我们期望的结构,包含model、quantity、type三个属性。constructor中的Object.assign(this, data)假定传入的data对象已经具有正确的键名。
- $.each(inputJson, function(index, value) { ... });: 这是一个迭代inputJson数组的循环。在每次迭代中,value代表数组中的当前JSON对象。
- const { HS: model, __EMPTY: quantity, __EMPTY_1: type } = value;: 这是实现键名映射的关键。它从当前的value对象中提取HS、__EMPTY、__EMPTY_1这三个属性的值,并将它们分别赋值给新声明的model、quantity、type常量。
- completeStock.push(new Stocks({ model, quantity, type }));: 在获得正确命名的属性值后,我们创建一个新的匿名对象{ model, quantity, type }(这是ES6的属性值简写语法,等同于{ model: model, quantity: quantity, type: type }),然后将其作为参数传递给Stocks类的构造函数,最终生成一个符合预期的Stocks实例并添加到completeStock数组中。
进阶考量与最佳实践
-
数据清洗与类型转换: 原始JSON数据可能包含多余的空格(如"HS": " RAS222PCAIBA")或需要特定类型转换(如"__EMPTY": 1可能需要确保是整数)。在解构赋值后,创建对象前,可以进行这些处理:
const completeStockCleaned = inputJson.map(item => { const { HS: modelRaw, __EMPTY: quantityRaw, __EMPTY_1: typeRaw } = item; return new Stocks({ model: modelRaw ? modelRaw.trim() : '', // 清除空格,处理空值 quantity: parseInt(quantityRaw) || 0, // 转换为整数,提供默认值 type: typeRaw ? typeRaw.trim() : '' }); }); console.log(completeStockCleaned); -
处理缺失键与默认值: 如果原始JSON中某个键可能不存在,解构赋值会将其对应的变量设为undefined。你可以为解构的变量提供默认值:
const { HS: model = '未知型号', __EMPTY: quantity = 0 } = value; -
使用Array.prototype.map: 相较于$.each,Array.prototype.map是JavaScript原生的数组迭代方法,它更符合函数式编程范式,并且直接返回一个新数组,代码通常更简洁高效:
const completeStockMap = inputJson.map(item => { const { HS: model, __EMPTY: quantity, __EMPTY_1: type } = item; return new Stocks({ model: model ? model.trim() : '', quantity: parseInt(quantity) || 0, type: type ? type.trim() : '' }); }); console.log(completeStockMap); 类构造函数的灵活性: 如果映射逻辑复杂,也可以考虑将部分映射逻辑封装到Stocks类的静态方法中,例如Stocks.fromJSON(jsonObject),但这通常在外部键名一致性较高时更适用。对于外部键名频繁不一致的情况,外部解构赋值通常更灵活。
总结
通过本教程,我们学习了如何利用ES6的解构赋值与属性重命名功能,优雅地解决JavaScript中JSON对象键名与目标类属性不匹配的问题。这种方法不仅使代码更加清晰、易读,而且提供了强大的灵活性来处理各种数据格式。结合数据清洗、类型转换和默认值处理,可以构建出健壮且高效的数据映射逻辑,从而提升应用程序的数据处理能力和代码质量。在处理外部API数据或需要将通用数据结构转换为特定领域模型时,解构赋值无疑是一个非常实用的工具。










