我正在尝试编写一个返回可以满足json.Marshaler
接口的函数的方法。我的理由是提供结构的不同表示。也许我正在接近这个完全错误。
func (api *Api) SiteList(c *gin.Context) {
var sites []db.Site
if err := api.db.Find(&sites).Error; err != nil {
}
var payload []json.Marshaler
for _, site := range sites {
payload = append(payload, site.ToApi())
}
c.JSON(http.StatusOK, payload)
}
我从这个函数得到的结果是列表中的项目数正确,但每个项目的值相同:
[
{
"key": "NZ7LCA9HQN3",
"name": "autumn-waterfall-1573"
},
{
"key": "NZ7LCA9HQN3",
"name": "autumn-waterfall-1573"
},
{
"key": "NZ7LCA9HQN3",
"name": "autumn-waterfall-1573"
},
{
"key": "NZ7LCA9HQN3",
"name": "autumn-waterfall-1573"
},
{
"key": "NZ7LCA9HQN3",
"name": "autumn-waterfall-1573"
}
]
最后,这是ToApi
实施:
type EncoderFunc func() ([]byte, error)
func (fn EncoderFunc) MarshalJSON() ([]byte, error) {
return fn()
}
func (site *Site) ToApi() json.Marshaler {
return EncoderFunc(func() ([]byte, error) {
var payload public.Site
payload.Name = site.Name
payload.Key = site.Key
data, err := json.Marshal(payload)
if err != nil {
return nil, err
}
return data, nil
})
}
答案 0 :(得分:5)
这看起来像是经典的封口。有一个相关的FAQ部分。
基本上,for-loop中的site
每次都有相同的地址。您的所有功能都将通过此地址关闭。因此,当您在for循环之后对其进行评估时,它将在相同(最后)地址上的值上重复调用MarshalJSON
。您可以通过在每次迭代中创建一个新值来纠正它:
for _, site := range sites {
site := site // Perfectly legal and idiomatic.
payload = append(payload, site.ToApi())
}
游乐场:https://play.golang.org/p/eFujC1hEyD
来自Effective Go的另一篇相关文档:
错误是在Go for循环中,循环变量被重用于每次迭代,因此req变量在所有goroutine中共享。那不是我们想要的。 (...)另一个解决方案是创建一个具有相同名称的新变量,如下例所示:
for req := range queue { req := req // Create new instance of req for the goroutine. sem <- 1 go func() { process(req) <-sem }() }
本节是关于goroutines的,但显然这也适用于所有闭包。