Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Map response to a struct using Golang

Tags:

go

I am attempting to map a response from an API to a struct using Golang.

The JSON that comes back when I view the link in the browser is below:

{
"GBP": 657.54
}

And I just want to map it to a simple struct like so:

type Price struct {
    Name  string
    Value float64
}

Here is my current code.

func FetchCoinPrice(fsym string, tsyms string) Price {
    url := fmt.Sprintf("https://min-api.cryptocompare.com/data/price?fsym=" + fsym + "&tsyms=" + tsyms)

    fmt.Println("Requesting data from " + url)

    price := Price{}

    // getting the data using http
    request, err := http.Get(url)
    if err != nil {
        log.Fatal(err.Error())
    }

    // Read the response body using ioutil
    body, err := ioutil.ReadAll(request.Body)
    if err != nil {
        log.Fatal(err.Error())
    }

    defer request.Body.Close()

    if request.StatusCode == http.StatusOK {
        json.Unmarshal(body, &price)
    }

    return price

}

At the moment all I receive is an empty struct, I know the link is bringing back the correct data and I've tested it in my browser.

like image 262
Elliot Pohath Avatar asked Jan 03 '18 16:01

Elliot Pohath


People also ask

Can I use struct as map key Golang?

Maps also allow structs to be used as keys. These structs should be compared with each other. A structure or struct in Golang is a user-defined type that allows to combine fields of different types into a single type. Iterating over a map: You can also run a loop to access and operate each map key individually.

What does map return in golang?

Maps in Go will return the zero value for the value type of the map when the requested key is missing. Because of this, you need an alternative way to differentiate a stored zero, versus a missing key.

What is struct Golang?

In Go programming, a structure or struct is a user-defined type to store a collection of different fields into a single field.


2 Answers

The mapping doesn't work that way. Instead, you should use a map:

    data := []byte(`{
       "GBP": 657.54
     }`)

    priceMap := map[string]float64{}
    err := json.Unmarshal(data, &priceMap)
    // Check your errors!
    if err != nil {
      log.Fatal(err.Error())
    }
    fmt.Println(priceMap)

This will print:

map[GBP:657.54]

You can then iterate over the map and build the struct you mentioned above, or just access the entry directly if you know the currency. eg: priceMap["GBP"]

You should really check your errors, especially if you're not getting the output you expect from Unmarshal.

like image 73
Marc Avatar answered Nov 02 '22 23:11

Marc


The problem is that the unmarshaler cannot guess that keys in a JSON object should correspond to some value in a struct. Golang JSON mapping simply doesn't work that way.

However, you can make your "Price" type implement json.Unmarshaler to deserialize a message into a map of floats (map[string]float64) then ensure the shape is right and populate the struct accordingly:

func (p *Price) UnmarshalJSON(bs []byte) error {
  kvs := map[string]float64{}
  err := json.Unmarshal(bs, &kvs)
  if err != nil {
    return err
  }
  if len(kvs) != 1 {
    return fmt.Errorf("expected 1 key, got %d", len(kvs))
  }
  for name, value := range kvs {
    p.Name, p.Value = name, value
  }
  return nil
}

func main() {
  jsonstr := `[{"GBP":657.54},{"USD":123.45}]`
  ps := []Price{}
  err := json.Unmarshal([]byte(jsonstr), &ps)
  if err != nil {
    panic(err)
  }
  // ps=[]main.Price{
  //   main.Price{Name:"GBP", Value:657.54},
  //   main.Price{Name:"USD", Value:123.45}
  // }
}
like image 32
maerics Avatar answered Nov 03 '22 00:11

maerics