Go语言如何解析数组元素为多类型无键的JSON响应并遍历条目?
问题解决方法
背后核心逻辑
Go标准库的encoding/json包解析JSON时,不需要强制要求键名映射:
- 解析JSON对象时,会按struct标签的键名匹配赋值
- 解析JSON数组时,会按元素顺序和目标变量的类型匹配赋值
你之前遍历失败的原因是:将JSON解析到map[string]interface{}后,bots对应的值是interface{}通用类型,必须先做类型断言转为数组类型才能操作,这种写法类型不安全,更推荐用下面的结构体方案实现。
方案1:快速实现(直接用二维接口切片)
直接定义和JSON结构匹配的结构体,bots字段声明为二维空接口切片,解析后按索引取值+类型断言即可:
package main import ( "encoding/json" "fmt" "net/http" "time" ) // 外层响应结构体,和JSON字段一一对应 type BotResponse struct { Total int `json:"_total"` Bots [][]interface{} `json:"bots"` } func main() { // 发起接口请求 resp, err := http.Get("https://api.twitchinsights.net/v1/bots/online") if err != nil { panic(err) } defer resp.Body.Close() // 解析JSON响应 var botResp BotResponse err = json.NewDecoder(resp.Body).Decode(&botResp) if err != nil { panic(err) } // 遍历所有bot条目 for _, botItem := range botResp.Bots { // 按索引取值+类型校验 botName, ok := botItem[0].(string) if !ok { continue } viewCountRaw, ok := botItem[1].(float64) if !ok { continue } timestampRaw, ok := botItem[2].(float64) if !ok { continue } onlineTime := time.Unix(int64(timestampRaw), 0) fmt.Printf("Bot名称:%s,在线观众:%d,上线时间:%s\n", botName, int(viewCountRaw), onlineTime.Format("2006-01-02 15:04:05")) } }
方案2:规范实现(自定义结构体实现反序列化接口)
如果需要类型安全、避免每次遍历做类型断言,可以让自定义的Bot结构体实现json.Unmarshaler接口,自动把数组元素映射到结构体字段:
package main import ( "encoding/json" "fmt" "net/http" "time" ) // 自定义Bot结构体,字段含义明确 type Bot struct { Name string ViewCount int OnlineTime time.Time } // 实现json.Unmarshaler接口,自定义数组到结构体的映射逻辑 func (b *Bot) UnmarshalJSON(data []byte) error { var rawEntry []interface{} if err := json.Unmarshal(data, &rawEntry); err != nil { return err } if len(rawEntry) != 3 { return fmt.Errorf("非法的bot条目长度:%d", len(rawEntry)) } // 逐个转换类型赋值 var ok bool b.Name, ok = rawEntry[0].(string) if !ok { return fmt.Errorf("bot名称类型非法") } viewCountRaw, ok := rawEntry[1].(float64) if !ok { return fmt.Errorf("观众数类型非法") } b.ViewCount = int(viewCountRaw) timestampRaw, ok := rawEntry[2].(float64) if !ok { return fmt.Errorf("时间戳类型非法") } b.OnlineTime = time.Unix(int64(timestampRaw), 0) return nil } // 外层响应结构体直接用[]Bot作为bots字段类型 type BotResponse struct { Total int `json:"_total"` Bots []Bot `json:"bots"` } func main() { // 发起请求逻辑和方案1一致 resp, err := http.Get("https://api.twitchinsights.net/v1/bots/online") if err != nil { panic(err) } defer resp.Body.Close() var botResp BotResponse err = json.NewDecoder(resp.Body).Decode(&botResp) if err != nil { panic(err) } // 遍历不需要再做类型断言,直接取结构体字段即可 for _, bot := range botResp.Bots { fmt.Printf("Bot名称:%s,在线观众:%d,上线时间:%s\n", bot.Name, bot.ViewCount, bot.OnlineTime.Format("2006-01-02 15:04:05")) } }
内容的提问来源于stack exchange,提问作者w3bprinz
相关产品推荐
相关产品推荐

