I am building a an application that consumes an API then also saves the json data to go lang structs then later I will make end points that will provide results for certain calculations. I have implemented consuming the API the challenging part is how to save the data in a way that go understands. Which is a proper approach?
The following is the JSON format when I make a request. The key
I am interested in is only Time Series (1min)
{
"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
", err)
} else {
data, _ := ioutil.ReadAll(response.Body)
// fmt.Println(string(data))
}
}
Unfortunately, this data is poorly structured for easy unmarshaling via the golang JSON infrastructure.
The general approach for unmarshaling data such as this example is to define a type (or set of types) which contain the structure you desire and implement the json.Unmarshaler
interface with logic to inspect the incoming structure and populate the desired structs manually.
For example:
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
", 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
}