解析JSON到Google Sheets时Google Apps Script报TypeError求助
问题分析与解决方案
错误含义解释
TypeError: Cannot read properties of undefined (reading 'forEach') 表示你尝试调用forEach方法的变量是undefined。在你的脚本中,这个变量就是arrayData——你错误地尝试从API返回的JSON中读取opportunities属性,但实际上API返回的是直接的数组,而非包含opportunities键的对象。
核心问题排查
- API数据结构不匹配:你从API拿到的是直接的机会数据数组(
[ {...}, {...} ]),但脚本里写了var arrayData = json['opportunities'];,导致arrayData变成undefined,自然无法调用forEach。 - 字段名不匹配:脚本里的多个字段(如
opportunityName、contact、probabilityofClose等)和API实际返回的字段名(如name、contactNames、probability)完全不一致,即使解决了forEach的问题,后续也会出现大量undefined值。 - 嵌套对象/数组未处理:API返回的
stage是对象、companies是数组,直接写入Sheet会显示[object Object],需要提取具体属性。
修正后的完整脚本
function myFunction() { var url = "https://example.com"; var params = { "headers": { "Authorization": 'Basic ' + Utilities.base64Encode(Username + ':' + Password), "Act-Database-Name": "Sample" } }; var response = UrlFetchApp.fetch(url, params); var code = response.getResponseCode(); var token = response.getContentText(); Logger.log(code); Logger.log(token); var params2 = { "headers": { "Authorization": 'Bearer ' + token } }; try { // 调用API var url2 = "https://example.com/api/opportunities"; var jsondata = UrlFetchApp.fetch(url2, params2); var code2 = jsondata.getResponseCode(); Logger.log(code2); var data = jsondata.getContentText(); var json = JSON.parse(data); // 直接使用API返回的数组(无需取opportunities属性) var arrayData = json; // 容错:如果返回不是数组,初始化空数组 if (!Array.isArray(arrayData)) { arrayData = []; Logger.log("API返回数据格式不符合预期,已初始化空数组"); } // 用于存储Sheet数据的数组 var arrayProperties = []; // 遍历机会数据,映射正确字段 arrayData.forEach(function (el) { // 处理嵌套字段:提取stage名称 var stageName = el.stage ? el.stage.name : ""; // 处理公司数组:取第一个公司名称 var companyName = el.companies && el.companies.length > 0 ? el.companies[0].name : ""; // 处理联系人数组:拼接所有联系人名称 var contactList = el.contacts && el.contacts.length > 0 ? el.contacts.map(c => c.displayName).join(", ") : ""; arrayProperties.push([ el.status || "", el.name || "", // 对应原脚本的opportunityName contactList || el.contactNames || "", // 对应原脚本的contact el.probability || "", // 对应原脚本的probabilityofClose el.weightedTotal || "", el.productTotal || "", // 对应原脚本的total el.edited || "", // 对应原脚本的editDate el.estimatedCloseDate || "", el.actualCloseDate || "", el.recordManager || "", "", // leadTime:API无此字段,留空 "", // accessLevel:API无此字段,留空 "", // associatedWith:API无此字段,留空 companyName || "", // 对应原脚本的company el.competitor || "", el.created || "", // 对应原脚本的createDate el.daysInStage || "", // 修正大小写:原脚本为daysinStage el.daysOpen || "", el.grossMargin || "", el.importDate || "", el.editedBy || "", // 对应原脚本的lastEditedBy el.openDate || "", "", // opportunityField2:API无此字段,留空 "", // opportunityField3:API无此字段,留空 "", // opportunityField4:API无此字段,留空 "", // opportunityField5:API无此字段,留空 "", // opportunityField6:API无此字段,留空 "", // opportunityField7:API无此字段,留空 "", // opportunityField8:API无此字段,留空 el.isPrivate || "", // 对应原脚本的private el.stage?.process?.name || "", // 提取process名称 "", // product:API无此字段,留空 el.reason || "", el.creator || "", // 对应原脚本的recordCreator "", // referredBy:API无此字段,留空 el.sourceId || "", // 修正大小写:原脚本为sourceid stageName, el.stageStartDate || "" ]); }); // 选择输出工作表 var ss = SpreadsheetApp.getActiveSpreadsheet(); var sheet = ss.getSheetByName('Current'); if (!sheet) { Logger.log("未找到名为'Current'的工作表"); return; } // 清空旧数据(可选) sheet.getRange(2, 1, sheet.getLastRow()-1, sheet.getLastColumn()).clearContent(); // 写入数据到Sheet if (arrayProperties.length > 0) { var numRows = arrayProperties.length; var numCols = arrayProperties[0].length; sheet.getRange(2, 1, numRows, numCols).setValues(arrayProperties.reverse()); } else { Logger.log("无数据可写入"); } } catch (error) { Logger.log("错误信息:" + error.toString()); } }
关键修正点说明
- 数组获取:将
var arrayData = json['opportunities'];改为var arrayData = json;,匹配API返回的直接数组结构。 - 字段映射:所有字段名替换为API文档中定义的名称,修正大小写错误(如
daysinStage→daysInStage)。 - 嵌套数据处理:对
stage、companies、contacts等嵌套对象/数组,提取具体显示值,避免Sheet中出现[object Object]。 - 容错处理:增加数组判断、字段空值默认处理、工作表存在性检查,避免因数据异常导致脚本崩溃。
内容的提问来源于stack exchange,提问作者Ryan
相关产品推荐
相关产品推荐

