检查 JSON 属性 是否存在

Checking if a JSON property exists

我正在从 Yahoo Finance 导入股票 JSON 数据,以使用自定义函数导入 Google 个人股票追踪器表格:

function _yahoofinance(ticker) {
  const url = 'https://query2.finance.yahoo.com/v10/finance/quoteSummary/' + encodeURI(ticker)
            + '?modules=price,assetProfile,summaryDetail,incomeStatementHistory,'
            + 'balanceSheetHistory,defaultKeyStatistics,financialData,calendarEvents,'
            + 'recommendationTrend,upgradeDowngradeHistory,majorHoldersBreakdown'
  ;

  const response = UrlFetchApp.fetch(url, { muteHttpExceptions: true });
  const responseCode = response.getResponseCode();
  
  if (responseCode === 200) {
    const quote = JSON.parse(response.getContentText());

    const price       = _isEmpty(quote.quoteSummary.result[0].price.regularMarketPrice)     ? '-' : quote.quoteSummary.result[0].price.regularMarketPrice.fmt; 
    const divYield    = _isEmpty(quote.quoteSummary.result[0].summaryDetail.dividendYield)  ? '-' : quote.quoteSummary.result[0].summaryDetail.dividendYield.raw * 100;
    const payoutRatio = _isEmpty(quote.quoteSummary.result[0].summaryDetail.payoutRatio)    ? '-' : quote.quoteSummary.result[0].summaryDetail.payoutRatio.raw * 100;
    const marketCap   = _isEmpty(quote.quoteSummary.result[0].summaryDetail.marketCap)      ? '-' : quote.quoteSummary.result[0].summaryDetail.marketCap.fmt;
    const fwdPE       = _isEmpty(quote.quoteSummary.result[0].summaryDetail.forwardPE)      ? '-' : quote.quoteSummary.result[0].summaryDetail.forwardPE.fmt;
    const revenue     = _isEmpty(quote.quoteSummary.result[0].financialData.totalRevenue)   ? '-' : quote.quoteSummary.result[0].financialData.totalRevenue.fmt;
    const revGrowth   = _isEmpty(quote.quoteSummary.result[0].financialData.revenueGrowth)  ? '-' : quote.quoteSummary.result[0].financialData.revenueGrowth.raw * 100;
    const earnGrowth  = _isEmpty(quote.quoteSummary.result[0].financialData.earningsGrowth) ? '-' : quote.quoteSummary.result[0].financialData.earningsGrowth.raw * 100;
    const freeCash    = _isEmpty(quote.quoteSummary.result[0].financialData.freeCashflow)   ? '-' : quote.quoteSummary.result[0].financialData.freeCashflow.fmt;


      return [[divYield, payoutRatio, marketCap, fwdPE, revenue, revGrowth, earnGrowth, freeCash]];
  }
}

function _isEmpty(obj) {
    return Object.keys(obj).length === undefined;
}

请查看示例 sheet here(选项卡 JSON)。

问题是:并非每个代码都有我从雅虎请求的每个 属性。我尝试了很多不同的方法来处理这个问题:

const payoutRatio = _isEmpty(quote.quoteSummary.result[0].summaryDetail.payoutRatio)

function _isEmpty(obj) {
    return Object.keys(obj).length === undefined;
}

但是 none 这行得通。在示例 sheet 中,我试图提取股票代码 0P0001703K.SW 的数据,这是一种 ETF,因此它不会具有股票所具有的所有属性。 Google 张 return 错误:

#Error! TypeError: Cannot read property 'totalRevenue' of undefined (line 19).

它快把我逼疯了 :D 我一直在尝试以多种方式捕捉此类错误。我的问题是:检查 属性 是否存在的正确方法是什么,如果不存在,只需 return - 并继续下一个 属性?非常感谢您的帮助!

[编辑] 解决方案:

function _yahoofinance(ticker) {
  const url = 'https://query2.finance.yahoo.com/v10/finance/quoteSummary/' + encodeURI(ticker)
            + '?modules=price,assetProfile,summaryDetail,incomeStatementHistory,'
            + 'balanceSheetHistory,defaultKeyStatistics,financialData,calendarEvents,'
            + 'recommendationTrend,upgradeDowngradeHistory,majorHoldersBreakdown'
  ;

  const response = UrlFetchApp.fetch(url, { muteHttpExceptions: true });
  const responseCode = response.getResponseCode();
  
  if (responseCode === 200) {
    const quote = JSON.parse(response.getContentText());

    const price       = quote.quoteSummary.result[0]?.price?.regularMarketPrice?.fmt     || '-'; 
    const divYield    = quote.quoteSummary.result[0]?.summaryDetail?.dividendYield?.raw  ?  '-' : quote.quoteSummary.result[0].summaryDetail.dividendYield.raw * 100;
    const payoutRatio = quote.quoteSummary.result[0]?.summaryDetail?.payoutRatio?.raw    ?  '-' : quote.quoteSummary.result[0].summaryDetail.payoutRatio.raw * 100;
    const revenue     = quote.quoteSummary.result[0]?.financialData?.totalRevenue?.fmt   || '-';

    return [[price, divYield, payoutRatio, revenue]];
  }
}

您可以使用 optional chaining 而不是 _isEmpty() 函数,因为您检查 quote.quoteSummary.result[0].financialData.totalRevenue 是否为空但错误显示 'totalRevenue' of undefined。这意味着 financialData 是未定义的,它甚至在进入您的 isEmpty 函数之前就会抛出错误。

在您的示例中,它类似于:

const quote = {
  quoteSummary: {
    result: [{}]
  }
}
const revenue = quote.quoteSummary.result?.[0]?.financialData?.totalRevenue?.fmt || '-';
console.log(revenue);