首页 > 解决方案 > 在 GO 语言中使用 API 时如何将 JSON 转换为 Go 类型定义

问题描述

我正在构建一个使用 API 的应用程序,然后还将 json 数据保存到 go lang 结构中,然后我将创建端点,为某些计算提供结果。我已经实现了使用 API,具有挑战性的部分是如何以一种可以理解的方式保存数据。哪种方法合适?

以下是我发出请求时的 JSON 格式。key我感兴趣的只是Time Series (1min)

JSON

{
    "Meta Data": {
        "1. Information": "Intraday (1min) prices and volumes",
        "2. Symbol": "MSFT",
        "3. Last Refreshed": "2018-05-24 16:00:00",
        "4. Interval": "1min",
        "5. Output Size": "Compact",
        "6. Time Zone": "US/Eastern"
    },
    "Time Series (1min)": {
        "2018-05-24 16:00:00": {
            "1. open": "98.3050",
            "2. high": "98.3600",
            "3. low": "98.2500",
            "4. close": "98.3100",
            "5. volume": "2377114"
        },
        "2018-05-24 15:59:00": {
            "1. open": "98.2900",
            "2. high": "98.3300",
            "3. low": "98.2900",
            "4. close": "98.3000",
            "5. volume": "137133"
        },
        "2018-05-24 15:58:00": {
            "1. open": "98.2900",
            "2. high": "98.3000",
            "3. low": "98.2600",
            "4. close": "98.2900",
            "5. volume": "135875"
        },
        "2018-05-24 15:53:00": {
            "1. open": "98.2750",
            "2. high": "98.2950",
            "3. low": "98.2600",
            "4. close": "98.2700",
            "5. volume": "77959"
        }
    }
}

代码

package main

import (
    "fmt"
    "io/ioutil"
    "net/http"
    "github.com/jmoiron/jsonq"
)

func main() {
    response, err := http.Get("https://www.alphavantage.co/query?function=TIME_SERIES_INTRADAY&symbol=MSFT&interval=1min&apikey=demo")
    if err != nil {
        fmt.Printf("The HTTP request failed with error %s\n", err)
    } else {
        data, _ := ioutil.ReadAll(response.Body)
        // fmt.Println(string(data))
    }
}

标签: jsongostruct

解决方案


不幸的是,这些数据的结构很差,无法通过 golang JSON 基础架构轻松解组。

解组数据(例如此示例)的一般方法是定义一个类型(或一组类型),其中包含您想要的结构并使用逻辑实现json.Unmarshaler接口以检查传入结构并手动填充所需的结构。

例如:

type Quote struct {
  Time                   string
  Open, High, Low, Close float32
  Volume                 int
}

type Quotes []Quote

func main() {
  qs := Quotes{}
  err := json.Unmarshal([]byte(jsonstr), &qs)
  if err != nil {
    panic(err)
  }
  fmt.Printf("%#v\n", qs)
}

const targetName = "Time Series (1min)"

func (qs *Quotes) UnmarshalJSON(bs []byte) error {
  // Unmarshal into a generic map
  obj := make(map[string]interface{})
  err := json.Unmarshal(bs, &obj)
  if err != nil {
    return err
  }

  // Find the target time series
  entries, ok := obj[targetName].(map[string]interface{})
  if !ok {
    return fmt.Errorf("cannot find entry with name %q", targetName)
  }

  // Parse a Quote object from each entry in the target object
  quotes := []Quote{}
  for timestamp, values := range entries {
    values, ok := values.(map[string]interface{})
    if !ok {
      return fmt.Errorf("value for %q is not an object", timestamp)
    }
    quote := Quote{}
    quote.Time = timestamp
    v, err := strconv.ParseFloat(values["1. open"].(string), 32)
    if err != nil {
      return err
    }
    quote.Open = float32(v)
    // Repeat for each of Close,High,Low,Volume...
    quotes = append(quotes, quote)
  }

  *qs = Quotes(quotes)
  return nil
}

推荐阅读