【问题标题】:retrieving data from json response from an API从 API 的 json 响应中检索数据
【发布时间】:2020-03-12 09:09:23
【问题描述】:

我试图通过将 json 响应存储在一些结构(机场 + 坐标)中来获取数据,但我不知道如何处理它,因为我对地图和接口不够好。代码显示没有错误,但 MapofAirports 完全为空,代码如下:

package main

import (
    //"api/client"
    //"api/client/clienterrors"
    //"api/client/openstreetmap"
    "encoding/json"
    "fmt"
    "io/ioutil"
    "log"
    "math"
    "net/http"
    "os"
    "strconv"
    "strings"
)

type Coordinates struct {
    Longitude string `json:"lon"`
    Latitude  string `json:"lat"`
}

type Airport struct {
    Co       Coordinates `json:"location"`
    IATACode string      `json:"id"`
    Distance float64     `json:"distance"` // distance to coordinates in kilometer
}

func GetCoordinatesFromURL(url string) (float64, float64) {

    parts := strings.Split(url, "=")

    lat0 := strings.Split(parts[2], "&")
    lon0 := strings.Split(parts[3], "&")

    lat1, _ := strconv.ParseFloat(lat0[0], 64)
    lon1, _ := strconv.ParseFloat(lon0[0], 64)

    return lat1, lon1
}

func CalcDistance(lat1 float64, long1 float64, lat2 float64, long2 float64) float64 {

    var latitude1 = lat1 * math.Pi / 180
    var latitude2 = lat2 * math.Pi / 180
    var longitude1 = long1 * math.Pi / 180
    var longitude2 = long2 * math.Pi / 180

    var R = 6371.0
    var d = R * math.Acos(math.Cos(latitude1)*math.Cos(latitude2)*math.Cos(longitude2-longitude1)+math.Sin(latitude1)*math.Sin(latitude2))

    return d
}

func main() {
    var Locations []Airport
    Locations = make([]Airport, 0)

    var url = fmt.Sprintf("https://api.skypicker.com/locations?type=radius&lat=40.730610&lon=-73.935242&radius=250&location_types=airport&limit=3&sort=id&active_only=true")

    UrlLat, UrlLon := GetCoordinatesFromURL(url)

    resp, err := http.Get(url)
    if err != nil {
        panic(err.Error())
    }
    defer resp.Body.Close()
    body, err := ioutil.ReadAll(resp.Body)

    var airportsJsonResponse interface{}

    err = json.Unmarshal(body, &airportsJsonResponse)

    MapofAirports, ok := airportsJsonResponse.([]interface{})

    if ok {
        lenAiroMap := len(MapofAirports)

        locationsMaps := make(map[int]map[string]interface{})

        for i := 0; i < lenAiroMap; i++ {
            locationsMaps[i] = MapofAirports[i].(map[string]interface{})
        }
        var coords Coordinates
        for i := 0; i < lenAiroMap; i++ {
            if longitude, ok0 := locationsMaps[i]["lon"].(string); ok0 {
                if latitude, ok1 := locationsMaps[i]["lat"].(string); ok1 {
                    coords = Coordinates{longitude, latitude}
                }
            }
            code := locationsMaps[i]["id"].(string)

            latFromCoordinates, _ := strconv.ParseFloat(Locations[i].Co.Latitude, 64)
            lonFromCoordinates, _ := strconv.ParseFloat(Locations[i].Co.Longitude, 64)

            dist := CalcDistance(latFromCoordinates, lonFromCoordinates, UrlLat, UrlLon)
            Locations = append(Locations, Airport{
                Co:       coords,
                IATACode: code,
                Distance: dist,
            })
        }
    }
    LocationsJson, err := json.Marshal(Locations)
    if err != nil {
        log.Fatal("Cannot encode to JSON ", err)
    }
    fmt.Fprintf(os.Stdout, "%s", LocationsJson)
}

screenshot of json response

在屏幕截图中,这是我们的 json 响应,我正在这样处理:

{ locations[],meta,last_refresh,results_retrieved } ==> location : { id , location + distance(calculated with a function) }

【问题讨论】:

  • 如果你预先知道 JSON 结构,并且愿意使用 Go 结构,那么不要使用映射和空接口。 play.golang.com/p/MG1tX8Zbumu
  • 如果数据结构已知,我也建议使用结构。你可以convert JSON to Go structs using online tools
  • @mkopriva 感谢您的回答,它真的很有效,但我仍然有一个唯一的问题,如果我想将 #Distance float64 json:"distance"# 添加到结构并想通过使用来获取值CalcDistance() 函数和 lon,lat 参数,我该怎么办?
  • @herotet 你可以让CalcDistance 成为可以访问坐标的类型的方法,无论是Location 还是LocationItem 都取决于你。如果您需要Distance 作为一个字段,那么您可以在完成解组后通过循环遍历已解组json 的结构实例来计算它,这非常容易。如果你想在解组的同时计算距离,你可以让你解组的类型实现json.Unmarshaler接口。
  • @herotet 这是一个解组后循环的示例play.golang.com/p/UTKItnPaXs3

标签: json api go interface maps


【解决方案1】:

把这行MapofAirports, ok := airportsJsonResponse.([]interface{})改成这个

MapofAirports, ok := airportsJsonResponse.(map[string]interface{})

如果您在该行放置一个断点,您将看到airportsJsonResponse 的类型为map[string]interface{}。 而且您必须将这些行更改为键值迭代

for i := 0; i < lenAiroMap; i++ {
            locationsMaps[i] = MapofAirports[i].(map[string]interface{})
        }

像下面这样:

        lenAiroMap := len(MapofAirports)
        locationsMaps := make([]map[string]interface{},lenAiroMap)
        for i, value := range MapofAirports["locations"].([]interface{}) {
            converted := value.(map[string]interface{})
            locationsMaps[i] = converted
        }

【讨论】:

    【解决方案2】:

    这是我的最后一次更新,在运行程序时它会在解组步骤中出现恐慌

    package main
    
    import (
        "encoding/json"
        "fmt"
        "io/ioutil"
        "math"
        "net/http"
        "strconv"
        "strings"
    )
    
    type Coordinates struct {
        Longitude string `json:"lon"`
        Latitude  string `json:"lat"`
    }
    
    type Airport struct {
        Co       Coordinates `json:"location"`
        IATACode string      `json:"id"`
        Distance float64     `json:"distance"` // distance to coordinates in kilometer
    }
    
    type Response struct {
        Locations []Airport `json:"locations"`
        // add all the other fields you care about
    }
    
    func GetCoordinatesFromURL(url string) (float64, float64) {
    
        parts := strings.Split(url, "=")
    
        lat0 := strings.Split(parts[2], "&")
        lon0 := strings.Split(parts[3], "&")
    
        lat1, _ := strconv.ParseFloat(lat0[0], 64)
        lon1, _ := strconv.ParseFloat(lon0[0], 64)
    
        return lat1, lon1
    }
    
    func CalcDistance(lat1 float64, long1 float64, lat2 float64, long2 float64) float64 {
    
        var latitude1 = lat1 * math.Pi / 180
        var latitude2 = lat2 * math.Pi / 180
        var longitude1 = long1 * math.Pi / 180
        var longitude2 = long2 * math.Pi / 180
    
        var R = 6371.0
        var d = R * math.Acos(math.Cos(latitude1)*math.Cos(latitude2)*math.Cos(longitude2-longitude1)+math.Sin(latitude1)*math.Sin(latitude2))
    
        return d
    }
    
    func main() {
    
    
        var url = fmt.Sprintf("https://api.skypicker.com/locations?type=radius&lat=40.730610&lon=-73.935242&radius=250&location_types=airport&limit=3&sort=id&active_only=true")
    
        UrlLat, UrlLon := GetCoordinatesFromURL(url)
    
        resp, err := http.Get(url)
        if err != nil {
            panic(err.Error())
        }
        defer resp.Body.Close()
    
         data, err := ioutil.ReadAll(resp.Body)
        res := &Response{}
        if err := json.Unmarshal(data, res); err != nil {
            panic(err)
        }
        fmt.Println(res.Locations)
    
        for i, item := range res.Locations {
            latt,_ := strconv.ParseFloat(item.Co.Latitude, 64)
            lonn,_ :=strconv.ParseFloat(item.Co.Longitude, 64)
            res.Locations[i].Distance = CalcDistance(latt,lonn , UrlLat, UrlLon)
        }
        fmt.Println("after calculate distance")
        fmt.Println(res.Locations)
    }
    

    这有什么问题?

    【讨论】:

    • goroutine 1 [运行]: main.main() C:/Users/herotet/go/src/CT_ProjectV2/main.go:70 +0x536
    • 当我调试时我得到这个:json: cannot unmarshal number into Go struct field Coordinates.locations.location.lat of type string
    • 将 Lat Lon 字段类型从 string 更改为 float64
    • 我还有一个唯一的问题,因为它是一个小组工作,其他人已经使用纬度和经度作为字符串,我应该怎么做我想要相同的结果但在这种情况下?
    • 您是否需要在Coordinates 类型中为string?或者您是否需要它在您发送回发出初始请求的客户端的响应 json 中为 string
    猜你喜欢
    • 2020-06-18
    • 1970-01-01
    • 1970-01-01
    • 2017-08-10
    • 2014-01-03
    • 1970-01-01
    • 2019-01-12
    • 1970-01-01
    • 2019-04-17
    相关资源
    最近更新 更多