提问者:小点点

为什么我从解码器得到一个空JSON结构?[关闭]


这个问题是由错别字或无法再复制的问题引起的。虽然类似的问题在这里可能是主题,但这个问题的解决方式不太可能帮助未来的读者。

我正在尝试对AlphaVantage的损益表endpoint进行API调用,然后将响应散集到我的IncomStmt结构中。它没有引发任何类型的错误,但是当我尝试打印结果时,我得到了

这是endpoint:https://www.alphavantage.co/query?function=INCOME_STATEMENT

下面是API响应结构:https://www.alphavantage.co/query?function=INCOME_STATEMENT

到目前为止,我已经梳理了我的IncomStmtQuarterlyReportAnnualReport结构,所有字段都与API中的名称和类型(所有字符串都符合上面API结构)相匹配。我检查了我是否正确导出了所有字段,我相信我的getJson函数工作正常,因为我已经能够使用该函数散集一个更简单的API响应。所以,我认为问题在于我如何创建要散集的结构。

我的预期结果是在GetIncomStmtfmt. Printf("UnmarshaledIncomStmt:%v\n",incomStmt)中看到打印出来的API响应。

下面的代码:

type AnnualReports struct {
   FiscalDateEnding                  string `json:"fiscalDateEnding"`
   ReportedCurrency                  string `json:"reportedCurrency"`
   GrossProfit                       string `json:"grossProfit"`
   TotalRevenue                      string `json:"totalRevenue"`
   CostOfRevenue                     string `json:"costOfRevenue"`
   CostOfGoodsAndServicesSold        string `json:"costOfGoodsAndServicesSold"`
   OperatingIncome                   string `json:"operatingIncome"`
   SellingGeneralAndAdministrative   string `json:"sellingGeneralAndAdministrative"`
   ResearchAndDevelopment            string `json:"researchAndDevelopment"`
   OperatingExpenses                 string `json:"operatingExpenses"`
   InvestmentIncomeNet               string `json:"investmentIncomeNet"`
   NetInterestIncome                 string `json:"netInterestIncome"`
   InterestIncome                    string `json:"interestIncome"`
   InterestExpense                   string `json:"interestExpense"`
   NonInterestIncome                 string `json:"nonInterestIncome"`
   OtherNonOperatingIncome           string `json:"otherNonOperatingIncome"`
   Depreciation                      string `json:"depreciation"`
   DepreciationAndAmortization       string `json:"depreciationAndAmortization"`
   IncomeBeforeTax                   string `json:"incomeBeforeTax"`
   IncomeTaxExpense                  string `json:"incomeTaxExpense"`
   InterestAndDebtExpense            string `json:"interestAndDebtExpense"`
   NetIncomeFromContinuingOperations string `json:"netIncomeFromContinuingOperations"`
   ComprehensiveIncomeNetOfTax       string `json:"comprehensiveIncomeNetOfTax"`
   Ebit                              string `json:"ebit"`
   Ebitda                            string `json:"ebitda"`
   NetIncome                         string `json:"netIncome"`
}

type QuarterlyReports struct {
   FiscalDateEnding                  string `json:"fiscalDateEnding"`
   ReportedCurrency                  string `json:"reportedCurrency"`
   GrossProfit                       string `json:"grossProfit"`
   TotalRevenue                      string `json:"totalRevenue"`
   CostOfRevenue                     string `json:"costOfRevenue"`
   CostOfGoodsAndServicesSold        string `json:"costOfGoodsAndServicesSold"`
   OperatingIncome                   string `json:"operatingIncome"`
   SellingGeneralAndAdministrative   string `json:"sellingGeneralAndAdministrative"`
   ResearchAndDevelopment            string `json:"researchAndDevelopment"`
   OperatingExpenses                 string `json:"operatingExpenses"`
   InvestmentIncomeNet               string `json:"investmentIncomeNet"`
   NetInterestIncome                 string `json:"netInterestIncome"`
   InterestIncome                    string `json:"interestIncome"`
   InterestExpense                   string `json:"interestExpense"`
   NonInterestIncome                 string `json:"nonInterestIncome"`
   OtherNonOperatingIncome           string `json:"otherNonOperatingIncome"`
   Depreciation                      string `json:"depreciation"`
   DepreciationAndAmortization       string `json:"depreciationAndAmortization"`
   IncomeBeforeTax                   string `json:"incomeBeforeTax"`
   IncomeTaxExpense                  string `json:"incomeTaxExpense"`
   InterestAndDebtExpense            string `json:"interestAndDebtExpense"`
   NetIncomeFromContinuingOperations string `json:"netIncomeFromContinuingOperations"`
   ComprehensiveIncomeNetOfTax       string `json:"comprehensiveIncomeNetOfTax"`
   Ebit                              string `json:"ebit"`
   Ebitda                            string `json:"ebitda"`
   NetIncome                         string `json:"netIncome"`
}

type IncomeStmt struct {
   Symbol           string             `json:"symbol"`
   AnnualReports    []AnnualReports    `json:"annualReports"`
   QuarterlyReports []QuarterlyReports `json:"quarterlyReports"`
}

type Client interface {
   GetEarnings(symbol string) (*Earnings, error)
   GetIncomeStmt(symbol string) (*IncomeStmt, error)
}

type ClientOpts struct {
   ApiKey     string
   BaseUrl    string
   HttpClient *http.Client
}

type client struct {
   opts       ClientOpts
   httpClient *http.Client
}

func NewClient(opts ClientOpts) Client {
   if opts.ApiKey == "" {
       opts.ApiKey = secretProvider.GetEnv("ALPHA_VANTAGE_API_KEY")
   }
   if opts.BaseUrl == "" {
       opts.BaseUrl = secretProvider.GetEnv("ALPHA_VANTAGE_API_BASE_URL")
   }

   httpClient := opts.HttpClient

   if httpClient == nil {
       httpClient = &http.Client{Timeout: 10 * time.Second}
   }

   return &client{
       opts:       opts,
       httpClient: httpClient,
   }
}

func (c *client) get(u *url.URL) (*http.Response, error) {
   req, err := http.NewRequest(http.MethodGet, u.String(), nil)
   if err != nil {
       return nil, err
   }

   resp, err := c.httpClient.Do(req)

   if err != nil {
       return nil, err
   }

   return resp, nil

}

// getJson will take the http response and unmarshal it, returning an error if unsuccessful
func getJson(resp *http.Response, data interface{}) error {
   defer resp.Body.Close()
   return json.NewDecoder(resp.Body).Decode(&data)
}

func (c *client) GetIncomeStmt(symbol string) (*IncomeStmt, error) {
   u, err := url.Parse(fmt.Sprintf("%s?function=INCOME_STATEMENT&symbol=%s&apiKey=%s", c.opts.BaseUrl, symbol, c.opts.ApiKey))
   if err != nil {
       return nil, err
   }
   resp, err := c.get(u)

   if err != nil {
       return nil, err
   }

   incomeStmt := &IncomeStmt{}

   if err = getJson(resp, incomeStmt); err != nil {
       fmt.Print(err)
       log.Fatal("Error decoding data in getJson fn.")
       return nil, err
   }

   fmt.Printf("Unmarshaled IncomeStmt: %v \n", incomeStmt)

   return incomeStmt, nil
}


共1个答案

匿名用户

这里的问题是我在endpointURL中错误地camelCase了apiKey。这样做会导致我的API密钥没有在GET请求中传递给AlphaVantage。我发现这要归功于Cerise上面的评论。当API返回200状态时(即使我的请求格式错误!),它促使我尝试使用ioutil. ReadAll()以不同的方式读取响应正文。需要注意的是,根据这篇文章,ReadAll()方法在实际使用中是不执行的。我仅将以下内容用于调试目的:

    body, err := ioutil.ReadAll(resp.Body)
    if err != nil {
        log.Fatal("Error using readall")
    }
    fmt.Println(string(body))

它打印了一直隐藏的错误消息:

{
    "Error Message": "the parameter apikey is invalid or missing. Please claim your free API key on (https://www.alphavantage.co/support/#api-key). It should take less than 20 seconds."
}

将查询参数apiKey降低到apikey导致我得到了预期的响应。这个故事的寓意是我需要添加额外的错误处理,以防API没有返回预期的响应体,这样程序就不会静默失败。