Go解析含type字段的JSON数组为结构体的问题及解决
解决Go Fiber中JSON数组解析报错:无法将数组反序列化为单个结构体类型
问题场景
处理POST请求时,请求体始终是包含type字段的JSON数组(示例如下),但解析时触发错误:
error = json: cannot unmarshal array into Go value of type main.Entry
无法修改请求体格式,目标是将其解析为结构体数组,循环处理后存入InfluxDB。
示例请求体:
[{"type":"sgv","device":"0EM48R5JQ","dateString":"2023-06-23T22:39:16.000+02:00","date":1687552756000,"sgv":127,"delta":-0.400,"direction":"Flat","noise":1,"filtered":127000,"unfiltered":127000,"rssi":100}]
原代码(存在问题)
package main import ( "fmt" "github.com/gofiber/fiber/v2" "github.com/gofiber/fiber/v2/middleware/logger" "github.com/gofiber/fiber/v2/middleware/compress" ) func main() { app := fiber.New() app.Use(logger.New(logger.Config{ Format: "[${ip}]:${port} ${status} - ${method} ${path} - ${reqHeaders}\n${body}\n", })) app.Use(compress.New(compress.Config{ Level: compress.LevelBestSpeed, // 1 })) type Entry struct { Type string `json:"type"` Device string `json:"device"` DateString string `json:"dateString"` Date string `json:"date"` Sgv string `json:"sgv"` Delta string `json:"delta"` Direction string `json:"direction"` Noise string `json:"noise"` Filtered string `json:"filtered"` Unfiltered string `json:"unfiltered"` Rssi string `json:"rssi"` } type Entries struct { Entries []Entry } app.Get("/", func(c *fiber.Ctx) error { return c.SendString("Hello, World!") }) app.Post("/api/v1/entries", func(c *fiber.Ctx) error { entry := new(Entry) if err := c.BodyParser(entry); err != nil { fmt.Println("error = ",err) return c.SendStatus(500) } return c.SendStatus(200) }) app.Post("/entries", func(c *fiber.Ctx) error { return c.SendString("OK") }) app.Post("/devicestatus", func(c *fiber.Ctx) error { return c.SendString("OK") }) app.Post("/treatments", func(c *fiber.Ctx) error { return c.SendString("OK") }) app.Listen(":3001") }
错误原因
- 解析目标类型不匹配:用单个
Entry结构体解析JSON数组,而JSON数组只能反序列化为切片/数组类型,导致类型不兼容报错。 - 字段类型不匹配:
Entry结构体中多个字段定义为string,但JSON对应值是数字(如date是时间戳整数、sgv是整数、delta是浮点数),会引发隐性解析错误。
修正后代码
package main import ( "fmt" "github.com/gofiber/fiber/v2" "github.com/gofiber/fiber/v2/middleware/compress" "github.com/gofiber/fiber/v2/middleware/logger" ) func main() { app := fiber.New() app.Use(logger.New(logger.Config{ Format: "[${ip}]:${port} ${status} - ${method} ${path} - ${reqHeaders}\n${body}\n", })) app.Use(compress.New(compress.Config{ Level: compress.LevelBestSpeed, // 1 })) type Entry struct { Type string `json:"type"` Device string `json:"device"` DateString string `json:"dateString"` Date int64 `json:"date"` // 修正为int64匹配时间戳数字 Sgv int `json:"sgv"` // 修正为int匹配整数 Delta float64 `json:"delta"` // 修正为float64匹配浮点数 Direction string `json:"direction"` Noise int `json:"noise"` // 修正为int匹配整数 Filtered int `json:"filtered"` // 修正为int匹配整数 Unfiltered int `json:"unfiltered"` // 修正为int匹配整数 Rssi int `json:"rssi"` // 修正为int匹配整数 } type Entries []Entry // 直接定义为Entry切片类型 app.Post("/api/v1/entries", func(c *fiber.Ctx) error { entries := new(Entries) // 创建切片指针用于解析数组 if err := c.BodyParser(entries); err != nil { fmt.Println("error:", err) return c.SendStatus(fiber.StatusInternalServerError) } // 循环处理每个条目,可在此添加存入InfluxDB的逻辑 for _, entry := range *entries { fmt.Println(entry) } return c.SendStatus(fiber.StatusOK) }) app.Listen(":3001") }
关键修改点
- 将
Entries定义为[]Entry切片类型,而非包含切片的结构体,匹配JSON数组格式。 - 使用
new(Entries)作为BodyParser的目标,实现数组解析。 - 修正
Entry结构体字段类型,完全匹配JSON中的数据类型,避免解析隐性错误。 - 新增循环遍历逻辑,可直接处理每个条目并写入InfluxDB。
内容的提问来源于stack exchange,提问作者JRSmile
相关产品推荐
相关产品推荐

