如何在解析前校验JSON数组有效数据以避免映射报错?
问题描述
我尝试在对JSON数组执行map映射操作前完成数据校验,避免后续处理报错,但一直没成功。比如需要校验data.data.map(x => x.movementHistory[amt].statusHistory.conciseStatus)或data.data.map(x => x.movementHistory[amt].description)这类数据路径的有效性,希望在处理前完成每个映射的前置校验。已经试过空值合并运算符??,但完全没用。
以下是我的Google Apps Script代码及示例JSON:
function ParseESTESShipmentStatus(movementhistory,amt) { var sheet = SpreadsheetApp.getActiveSheet(); //test mode movementhistory = sheet.getRange("A10").getValue(); amt = 0; var data = JSON.parse(movementhistory); var id = data.data.map(x => x.movementHistory[amt].description) ?? "test" Logger.log (id) var description = data.data.map(x => x.movementHistory[amt].description) var transportEventTypeCode = data.data.map(x => x.movementHistory[amt].transportEventTypeCode) var conciseStatus = data.data.map(x => x.movementHistory[amt].statusHistory.conciseStatus) var expandedStatus = data.data.map(x => x.movementHistory[amt].statusHistory.expandedStatus) var referenceDate = data.data.map(x => x.movementHistory[amt].statusHistory.referenceDate) var referenceTime = data.data.map(x => x.movementHistory[amt].statusHistory.referenceTime) var isException = data.data.map(x => x.movementHistory[amt].statusHistory.isException) var locationname = data.data.map(x => x.movementHistory[amt].location) var locationcode = data.data.map(x => x.movementHistory[amt].location.code) var address = data.data.map(x => x.movementHistory[amt].location.line) var city = data.data.map(x => x.movementHistory[amt].location.city) var state = data.data.map(x => x.movementHistory[amt].location.state) var postalCode = data.data.map(x => x.movementHistory[amt].location.postalCode) var telephone = data.data.map(x => x.movementHistory[amt].contact.telephone) Logger.log(telephone) }
示例JSON:
{ "data": [ { "movementHistory": [ { "id": "Pickup", "description": "Actual Pickup", "transportEventTypeCode": "Actual Pickup", "statusHistory": [ { "conciseStatus": "Departed Pickup Location", "expandedStatus": "Pickup Location Serviced", "referenceDate": "2022-11-10", "referenceTime": "16:26:33-06:00", "isException": false } ] }, { "id": "144", "description": "Origin Terminal", "location": { "name": "Waterloo", "code": "WTL", "address": { "line": [ "2125 Commercial Street" ], "city": "Waterloo", "state": "IA", "postalCode": "50702" }, "geoCoordinates": [ "-92.323624", "42.484943" ] }, "contact": { "telephone": "(319) 226-6414", "fax": "13192266420" }, "transportEventTypeCode": "Origin Terminal", "statusHistory": [ { "conciseStatus": "In Transit", "expandedStatus": "Loaded at Estes facility at Waterloo, IA (144)", "referenceDate": "2022-11-10", "referenceTime": "19:07:42-06:00", "isException": false }, { "conciseStatus": "In Transit", "expandedStatus": "Trailer Closed at Estes facility at Waterloo, IA (144)", "referenceDate": "2022-11-10", "referenceTime": "20:31:37-06:00", "isException": false }, { "conciseStatus": "In Transit", "expandedStatus": "Departed Estes facility at Waterloo, IA (144)", "referenceDate": "2022-11-10", "referenceTime": "21:00:55-06:00", "isException": false } ] }, { "id": "137", "description": "Planned Waypoint", "location": { "name": "Des Moines", "code": "DMO", "address": { "line": [ "2201 East Ovid Avenue" ], "city": "Des Moines", "state": "IA", "postalCode": "50313" }, "geoCoordinates": [ "-93.578027", "41.625336" ] }, "contact": { "telephone": "(515) 266-4910", "fax": "15152660292" }, "transportEventTypeCode": "Planned Waypoint", "statusHistory": [ { "conciseStatus": "In Transit", "expandedStatus": "En route to Estes facility at Des Moines, IA (137)", "referenceDate": "2022-11-10", "referenceTime": "21:00:55-06:00", "isException": false }, { "conciseStatus": "In Transit", "expandedStatus": "Arrived at Estes facility at Des Moines, IA (137)", "referenceDate": "2022-11-10", "referenceTime": "23:04:00-06:00", "isException": false }, { "conciseStatus": "In Transit", "expandedStatus": "Loaded at Estes facility at Des Moines, IA (137)", "referenceDate": "2022-11-11", "referenceTime": "00:48:46-06:00", "isException": false }, { "conciseStatus": "In Transit", "expandedStatus": "Trailer Closed at Estes facility at Des Moines, IA (137)", "referenceDate": "2022-11-11", "referenceTime": "01:17:36-06:00", "isException": false }, { "conciseStatus": "In Transit", "expandedStatus": "Departed Estes facility at Des Moines, IA (137)", "referenceDate": "2022-11-11", "referenceTime": "02:19:12-06:00", "isException": false } ] }, { "id": "133", "description": "Destination Terminal", "location": { "name": "Mankato", "code": "MNK", "address": { "line": [ "3495 3rd Avenue North" ], "city": "Mankato", "state": "MN", "postalCode": "56001" }, "geoCoordinates": [ "-93.994492", "44.205618" ] }, "contact": { "telephone": "(507) 345-5418", "fax": "15073456873" }, "transportEventTypeCode": "Destination Terminal", "statusHistory": [ { "conciseStatus": "In Transit", "expandedStatus": "En route to delivery facility at Mankato, MN (133)", "referenceDate": "2022-11-11", "referenceTime": "02:19:13-06:00", "isException": false }, { "conciseStatus": "In Transit", "expandedStatus": "Arrived at Mankato, MN (133) delivery facility", "referenceDate": "2022-11-11", "referenceTime": "05:39:42-06:00", "isException": false }, { "conciseStatus": "In Transit", "expandedStatus": "Loaded for delivery at Mankato, MN (133) delivery facility", "referenceDate": "2022-11-11", "referenceTime": "08:54:51-06:00", "isException": false }, { "conciseStatus": "In Transit", "expandedStatus": "Trailer Closed for delivery at Mankato, MN (133) delivery facility", "referenceDate": "2022-11-11", "referenceTime": "08:54:51-06:00", "isException": false }, { "conciseStatus": "In Transit", "expandedStatus": "Assigned to Driver at Mankato, MN (133) delivery facility", "referenceDate": "2022-11-11", "referenceTime": "08:55:49-06:00", "isException": false } ] }, { "id": "Delivery", "description": "Actual Delivery", "transportEventTypeCode": "Delivery", "statusHistory": [ { "conciseStatus": "Out for Delivery", "expandedStatus": "Out for delivery", "referenceDate": "2022-11-11", "referenceTime": "09:15:48-06:00", "isException": false }, { "conciseStatus": "Out for Delivery", "expandedStatus": "Arrived at delivery location", "referenceDate": "2022-11-11", "referenceTime": "14:09:26-06:00", "isException": false }, { "conciseStatus": "Out for Delivery", "expandedStatus": "Unloaded at delivery location", "referenceDate": "2022-11-11", "referenceTime": "14:09:27-06:00", "isException": false }, { "conciseStatus": "Delivered", "expandedStatus": "Delivery Completed - OK", "referenceDate": "2022-11-11", "referenceTime": "14:15:15-06:00", "reasonCode": "OK", "reason": "Delivery Completed - OK", "isException": false } ] } ], "disclaimers": [ "Freight Charges are subject to change upon audit.", "Freight Charges are shown on shipments for which the logged in user is the payor of the freight charges.", "Reported Delivery Date is subject to change upon verification.", "Reported delivery time is subject to change based upon verification." ] } ], "error": { "code": 0, "message": "", "details": "" } }
解决方案
为什么空值合并运算符没用?
你写的data.data.map(...) ?? "test"里,??是在整个map执行完之后才生效,但如果map回调里的属性访问路径断了(比如某个x没有movementHistory[amt],或者movementHistory[amt]里没有statusHistory),代码会直接抛出Cannot read property 'xxx' of undefined错误,根本走不到??那一步。
三种可行的解决方式
1. 用可选链运算符(最简单)
如果你的Google Apps Script环境支持ES2020+(现在基本都支持),直接用?.在每个可能为undefined的属性后面标记,配合??给缺失的属性设默认值:
function ParseESTESShipmentStatus(movementhistory, amt) { var sheet = SpreadsheetApp.getActiveSheet(); // test mode movementhistory = sheet.getRange("A10").getValue(); amt = 0; var data = JSON.parse(movementhistory); // 安全访问每个属性,缺失时返回默认值 var descriptions = data.data.map(x => x?.movementHistory?.[amt]?.description ?? "N/A"); var transportEventTypeCodes = data.data.map(x => x?.movementHistory?.[amt]?.transportEventTypeCode ?? "N/A"); // statusHistory是数组,所以要加[0]访问第一个元素 var conciseStatuses = data.data.map(x => x?.movementHistory?.[amt]?.statusHistory?.[0]?.conciseStatus ?? "N/A"); var expandedStatuses = data.data.map(x => x?.movementHistory?.[amt]?.statusHistory?.[0]?.expandedStatus ?? "N/A"); var referenceDates = data.data.map(x => x?.movementHistory?.[amt]?.statusHistory?.[0]?.referenceDate ?? "N/A"); var referenceTimes = data.data.map(x => x?.movementHistory?.[amt]?.statusHistory?.[0]?.referenceTime ?? "N/A"); var isExceptions = data.data.map(x => x?.movementHistory?.[amt]?.statusHistory?.[0]?.isException ?? false); var locationNames = data.data.map(x => x?.movementHistory?.[amt]?.location?.name ?? "N/A"); var locationCodes = data.data.map(x => x?.movementHistory?.[amt]?.location?.code ?? "N/A"); var addresses = data.data.map(x => x?.movementHistory?.[amt]?.location?.address?.line?.[0] ?? "N/A"); var cities = data.data.map(x => x?.movementHistory?.[amt]?.location?.address?.city ?? "N/A"); var states = data.data.map(x => x?.movementHistory?.[amt]?.location?.address?.state ?? "N/A"); var postalCodes = data.data.map(x => x?.movementHistory?.[amt]?.location?.address?.postalCode ?? "N/A"); var telephones = data.data.map(x => x?.movementHistory?.[amt]?.contact?.telephone ?? "N/A"); Logger.log(descriptions); Logger.log(telephones); }
2. 写个安全访问工具函数(兼容旧环境)
如果你的环境不支持可选链,就手动写个工具函数逐层校验属性:
// 安全访问深层属性,支持数组索引,比如"movementHistory[0].description" function safeGet(obj, path, defaultValue) { let current = obj; for (const key of path.split('.')) { // 处理数组索引格式 const indexMatch = key.match(/(\w+)\[(\d+)\]/); if (indexMatch) { const arrKey = indexMatch[1]; const index = parseInt(indexMatch[2]); // 校验数组是否存在且索引有效 if (!current || !current[arrKey] || !Array.isArray(current[arrKey]) || index >= current[arrKey].length) { return defaultValue; } current = current[arrKey][index]; } else { // 校验对象属性是否存在 if (!current || !(key in current)) { return defaultValue; } current = current[key]; } } return current ?? defaultValue; } function ParseESTESShipmentStatus(movementhistory, amt) { var sheet = SpreadsheetApp.getActiveSheet(); // test mode movementhistory = sheet.getRange("A10").getValue(); amt = 0; var data = JSON.parse(movementhistory); // 用工具函数安全获取属性 var descriptions = data.data.map(x => safe
相关产品推荐
相关产品推荐

