Golang unmarshaling array gives runtime error: index out of range
Golang unmarshaling array gives runtime error: index out of range
我是 golang 的新手,还在学习一些东西,但我偶然发现这个 issue.I 有 json 我从这个 api 获得的数据。 json 的相关部分如下所示:
{
"features": [
{
"properties": {
"display_name": "name",
"address": {"county": "County", "country":"country", "country_code":"cc"}
},
"bbox": [13.9171885,44.2603464,15.2326512,45.6729436],
"geometry": {
"coordinates": [
[
[
[14.4899021,41.4867039],
[14.5899021,41.5867039]
]
],
[
[
[15.4899021,41.4867039],
[15.5899021,41.5867039]
]
],
]
}
}
]
}
我正在尝试用这种结构解组它:
// Feature struct
type Feature struct {
Properties struct {
Name string `json:"display_name"`
Address struct {
Country string `json:"country"`
Code string `json:"country_code"`
} `json:"address"`
} `json:"properties"`
Bbox []float64 `json:"bbox"`
Geo struct {
Coordinates [][][][]float64 `json:"coordinates"`
} `json:"geometry"`
}
// GeoJSONEntry struct
type GeoJSONEntry struct {
Features []Feature `json:"features"`
}
我正在调用 api 函数:
func (server *Server) ImportRegionsMultiPolygon(w http.ResponseWriter, r *http.Request) {
var locations []LocationMulti
var errors []error
for _, county := range multiPolygons {
res, err := http.Get(baseURL + "?osm_ids=" + county.Type + county.OsmID + "&format=json&polygon_geojson=1")
if err != nil {
errors = append(errors, err)
}
bytes, err := ioutil.ReadAll(res.Body)
if err != nil {
errors = append(errors, err)
}
location, err := ParseGeoJSON(bytes)
if err != nil {
errors = append(errors, err)
} else {
locations = append(locations, location)
}
}
if errors != nil {
http.Error(w, errors[0].Error(), http.StatusBadRequest)
return
} else {
file, _ := json.MarshalIndent(locations, "", " ")
if err := ioutil.WriteFile("./static/regions/geodata-multi.json", file, 0644); err != nil {
http.Error(w, "Error writing file", http.StatusBadRequest)
return
} else {
responses.JSON(w, http.StatusCreated, locations)
}
}
}
其中 LocationMulti
看起来像这样:
// LocationMulti struct
type LocationMulti struct {
Name string
Lat string
Lng string
Country string
CountryCode string
Coordinates [][][][]float64
}
函数 ParseGeoJSON
看起来像这样:
func ParseGeoJSON(bytes []byte) (LocationMulti, error) {
var entry GeoJSONEntry
var err error
if err := json.Unmarshal(bytes, &entry); err != nil {
fmt.Println("Error parsing json", err)
}
fmt.Printf("%v\n", &entry)
location := LocationMulti{
Name: entry.Features[0].Properties.Name,
Lat: fmt.Sprintf("%f", (entry.Features[0].Bbox[1]+entry.Features[0].Bbox[3])/2),
Lng: fmt.Sprintf("%f", (entry.Features[0].Bbox[0]+entry.Features[0].Bbox[2])/2),
Country: entry.Features[0].Properties.Address.Country,
CountryCode: entry.Features[0].Properties.Address.Code,
Coordinates: entry.Features[0].Geo.Coordinates,
}
return location, err
}
我得到一个错误:
Error parsing json json: cannot unmarshal array into Go value of type controllers.Entry
2020/04/28 17:36:39 http: panic serving [::1]:61457: runtime error: index out of range
我做错了什么,我应该如何解组这种json?
正如@mkopriva 所指出的,问题可能出在检索 bytes
中。以下产生预期的输出:
package main
import (
"encoding/json"
"fmt"
)
type Feature struct {
Properties struct {
Name string `json:"display_name"`
Address struct {
Country string `json:"country"`
Code string `json:"country_code"`
} `json:"address"`
} `json:"properties"`
Bbox []float64 `json:"bbox"`
Geo struct {
Coordinates [][][]float64 `json:"coordinates"`
} `json:"geometry"`
}
type Entry struct {
Features []Feature `json:"features"`
}
var data = `{
"features": [
{
"properties": {
"display_name": "name",
"address": {"county": "County", "country":"country", "country_code":"cc"}
},
"bbox": [13.9171885,44.2603464,15.2326512,45.6729436],
"geometry": {
"coordinates": [
[
[14.4899021,41.4867039],
[14.5899021,41.5867039]
]
]
}
}
]
}`
func main() {
var entry Entry
bytes := []byte(data)
if err := json.Unmarshal(bytes, &entry); err != nil {
fmt.Println("Error parsing json", err)
}
fmt.Printf("%v\n", entry)
}
结果:
{[{{name {country cc}} [13.9171885 44.2603464 15.2326512 45.6729436] {[[[14.4899021 41.4867039] [14.5899021 41.5867039]]]}}]}
Unmarshal 方法本身调用正确,问题是您正在请求 JSON format which is an array instead of expected GeoJSON。 json.Unmarshal
返回错误的解决方案是替换
res, err := http.Get(baseURL + "?osm_ids=" + county.Type + county.OsmID + "&format=json&polygon_geojson=1")
和
res, err := http.Get(baseURL + "?osm_ids=" + county.Type + county.OsmID + "&format=geojson&polygon_geojson=1")
编辑:虽然另外我会重写 ParseGeoJSON
以避免恐慌:
func ParseGeoJSON(bytes []byte) (LocationMulti, error) {
var entry Entry
var err error
if err := json.Unmarshal(bytes, &entry); err != nil {
fmt.Println("Error parsing json", err)
return LocationMulti{}, err
}
fmt.Printf("%v\n", &entry)
if len(entry.Features) == 0 {
return LocationMulti{}, fmt.Errorf("parsed entry has no features")
}
feature := entry.Features[0]
if len(feature.Bbox) < 4 {
return LocationMulti{}, fmt.Errorf("bbox of parsed entry has too few points")
}
location := LocationMulti{
Name: feature.Properties.Name,
Lat: fmt.Sprintf("%f", (feature.Bbox[1]+feature.Bbox[3])/2),
Lng: fmt.Sprintf("%f", (feature.Bbox[0]+feature.Bbox[2])/2),
Country: feature.Properties.Address.Country,
CountryCode: feature.Properties.Address.Code,
Coordinates: feature.Geo.Coordinates,
}
return location, err
}
我是 golang 的新手,还在学习一些东西,但我偶然发现这个 issue.I 有 json 我从这个 api 获得的数据。 json 的相关部分如下所示:
{
"features": [
{
"properties": {
"display_name": "name",
"address": {"county": "County", "country":"country", "country_code":"cc"}
},
"bbox": [13.9171885,44.2603464,15.2326512,45.6729436],
"geometry": {
"coordinates": [
[
[
[14.4899021,41.4867039],
[14.5899021,41.5867039]
]
],
[
[
[15.4899021,41.4867039],
[15.5899021,41.5867039]
]
],
]
}
}
]
}
我正在尝试用这种结构解组它:
// Feature struct
type Feature struct {
Properties struct {
Name string `json:"display_name"`
Address struct {
Country string `json:"country"`
Code string `json:"country_code"`
} `json:"address"`
} `json:"properties"`
Bbox []float64 `json:"bbox"`
Geo struct {
Coordinates [][][][]float64 `json:"coordinates"`
} `json:"geometry"`
}
// GeoJSONEntry struct
type GeoJSONEntry struct {
Features []Feature `json:"features"`
}
我正在调用 api 函数:
func (server *Server) ImportRegionsMultiPolygon(w http.ResponseWriter, r *http.Request) {
var locations []LocationMulti
var errors []error
for _, county := range multiPolygons {
res, err := http.Get(baseURL + "?osm_ids=" + county.Type + county.OsmID + "&format=json&polygon_geojson=1")
if err != nil {
errors = append(errors, err)
}
bytes, err := ioutil.ReadAll(res.Body)
if err != nil {
errors = append(errors, err)
}
location, err := ParseGeoJSON(bytes)
if err != nil {
errors = append(errors, err)
} else {
locations = append(locations, location)
}
}
if errors != nil {
http.Error(w, errors[0].Error(), http.StatusBadRequest)
return
} else {
file, _ := json.MarshalIndent(locations, "", " ")
if err := ioutil.WriteFile("./static/regions/geodata-multi.json", file, 0644); err != nil {
http.Error(w, "Error writing file", http.StatusBadRequest)
return
} else {
responses.JSON(w, http.StatusCreated, locations)
}
}
}
其中 LocationMulti
看起来像这样:
// LocationMulti struct
type LocationMulti struct {
Name string
Lat string
Lng string
Country string
CountryCode string
Coordinates [][][][]float64
}
函数 ParseGeoJSON
看起来像这样:
func ParseGeoJSON(bytes []byte) (LocationMulti, error) {
var entry GeoJSONEntry
var err error
if err := json.Unmarshal(bytes, &entry); err != nil {
fmt.Println("Error parsing json", err)
}
fmt.Printf("%v\n", &entry)
location := LocationMulti{
Name: entry.Features[0].Properties.Name,
Lat: fmt.Sprintf("%f", (entry.Features[0].Bbox[1]+entry.Features[0].Bbox[3])/2),
Lng: fmt.Sprintf("%f", (entry.Features[0].Bbox[0]+entry.Features[0].Bbox[2])/2),
Country: entry.Features[0].Properties.Address.Country,
CountryCode: entry.Features[0].Properties.Address.Code,
Coordinates: entry.Features[0].Geo.Coordinates,
}
return location, err
}
我得到一个错误:
Error parsing json json: cannot unmarshal array into Go value of type controllers.Entry
2020/04/28 17:36:39 http: panic serving [::1]:61457: runtime error: index out of range
我做错了什么,我应该如何解组这种json?
正如@mkopriva 所指出的,问题可能出在检索 bytes
中。以下产生预期的输出:
package main
import (
"encoding/json"
"fmt"
)
type Feature struct {
Properties struct {
Name string `json:"display_name"`
Address struct {
Country string `json:"country"`
Code string `json:"country_code"`
} `json:"address"`
} `json:"properties"`
Bbox []float64 `json:"bbox"`
Geo struct {
Coordinates [][][]float64 `json:"coordinates"`
} `json:"geometry"`
}
type Entry struct {
Features []Feature `json:"features"`
}
var data = `{
"features": [
{
"properties": {
"display_name": "name",
"address": {"county": "County", "country":"country", "country_code":"cc"}
},
"bbox": [13.9171885,44.2603464,15.2326512,45.6729436],
"geometry": {
"coordinates": [
[
[14.4899021,41.4867039],
[14.5899021,41.5867039]
]
]
}
}
]
}`
func main() {
var entry Entry
bytes := []byte(data)
if err := json.Unmarshal(bytes, &entry); err != nil {
fmt.Println("Error parsing json", err)
}
fmt.Printf("%v\n", entry)
}
结果:
{[{{name {country cc}} [13.9171885 44.2603464 15.2326512 45.6729436] {[[[14.4899021 41.4867039] [14.5899021 41.5867039]]]}}]}
Unmarshal 方法本身调用正确,问题是您正在请求 JSON format which is an array instead of expected GeoJSON。 json.Unmarshal
返回错误的解决方案是替换
res, err := http.Get(baseURL + "?osm_ids=" + county.Type + county.OsmID + "&format=json&polygon_geojson=1")
和
res, err := http.Get(baseURL + "?osm_ids=" + county.Type + county.OsmID + "&format=geojson&polygon_geojson=1")
编辑:虽然另外我会重写 ParseGeoJSON
以避免恐慌:
func ParseGeoJSON(bytes []byte) (LocationMulti, error) {
var entry Entry
var err error
if err := json.Unmarshal(bytes, &entry); err != nil {
fmt.Println("Error parsing json", err)
return LocationMulti{}, err
}
fmt.Printf("%v\n", &entry)
if len(entry.Features) == 0 {
return LocationMulti{}, fmt.Errorf("parsed entry has no features")
}
feature := entry.Features[0]
if len(feature.Bbox) < 4 {
return LocationMulti{}, fmt.Errorf("bbox of parsed entry has too few points")
}
location := LocationMulti{
Name: feature.Properties.Name,
Lat: fmt.Sprintf("%f", (feature.Bbox[1]+feature.Bbox[3])/2),
Lng: fmt.Sprintf("%f", (feature.Bbox[0]+feature.Bbox[2])/2),
Country: feature.Properties.Address.Country,
CountryCode: feature.Properties.Address.Code,
Coordinates: feature.Geo.Coordinates,
}
return location, err
}