How can we parse an arbitrary JSON structure? In Go you can use an alternative solution to structs.
It is possible to represent the JSON structure with a map whose keys are of type string and whose values are of type interface.
var results map[string]interface{}
This representation can for example be used with a REST API:
package main
import (
"encoding/json"
"errors"
"fmt"
"io"
"net/http"
"net/url"
)
func apiRequest(query string) (map[string]interface{}, error) {
var results map[string]interface{}
baseURL := "https://api.unsplash.com"
resource := "/search/photos"
params := url.Values{}
params.Add("query", query)
params.Add("client_id", "api_key")
uri, _ := url.ParseRequestURI(baseURL)
uri.Path = resource
uri.RawQuery = params.Encode()
urlStr := fmt.Sprintf("%v", uri)
res, err := http.Get(urlStr)
if err != nil {
return results, err
}
defer res.Body.Close()
if res.StatusCode != 200 {
return results, errors.New("Request error")
}
body, and := io.ReadAll(res.Body)
if e != nil {
return results, e
}
jsonData := string(body)
jsonErr := json.Unmarshal([]byte(jsonData), &results)
if jsonErr != nil {
return results, jsonErr
}
return results, nil
}
func main() {
results, err := apiRequest("cat")
if err != nil {
panic(err)
}
fmt.Println(results)
}
In the example we are performing a keyword search with the Unsplash API. The response format is a very complex JSON object that we cannot map to structs in advance. Our generic map, however, allows us to still have a structure on which we can possibly iterate after the response JSON string is passed as a byte slice to the Unmarshal() function.
This is ultimately a useful solution when the JSON structure is very complex.