英文:
What's a clean solution for supporting dynamic struct fields in Go?
问题
计划重构去年构建的应用程序的代码库。假设我们有一个支持包含相关资源的 API 端点 GET /api/leagues?include=sport
。在 API 响应中,有没有更简洁的方法使 league
资源的 sport
属性动态化?sport
属性的值可以是整数或 Sport 结构。目前,我们正在这样做:
// models/league.go
type League struct {
SportId int64 `json:"-"`
Sport Sport `json:"-"`
SportWrapper interface{} `json:"sport"`
}
// controllers/league.go
include := c.Request.URL.Query().Get("include")
includes := strings.Split(include, ",")
for _, include := range includes {
if include == "sport" {
includeSport = true
}
}
if includeSport {
league.SportWrapper = league.Sport
} else {
league.SportWrapper = league.SportId
}
c.JSON(http.StatusOK, league)
API 请求的响应:
{
"sport": {
"id": "<id>",
"name": "<name>"
}
}
也可以是:
{
"sport": "<sport_id>"
}
希望这能帮到你!
英文:
Planning to refactor the codebase of an app we built last year. Let's say we have this api endpoint w/c supports including a related resource GET /api/leagues?include=sport
. Is there a cleaner way to make the sport
attribute of the league
resource dynamic in the api response? The value of the sport attribute can be an int or Sport struct. Currently, this is what we're doing:
// models/league.go
type League struct {
SportId int64 `json:"-"`
Sport Sport `json:"-"`
SportWrapper interface{} `json:"sport"`
}
// controllers/league.go
include := c.Request.URL.Query().Get("include")
includes := strings.Split(include, ",")
for _, include := range includes {
if include == "sport" {
includeSport = true
}
}
if includeSport {
league.SportWrapper = league.Sport
} else {
league.SportWrapper = league.SportId
}
c.JSON(http.StatusOK, league)
response to the api request:
{
sport: {
id: <id>,
name: <name>
}
}
it can also be:
{
sport: <sport_id>
}
答案1
得分: 1
没有更多的上下文很难说。我假设SportId也是Sport结构的一部分。你基本上只返回ID或整个结构。
我建议只添加以下结构:
Sport Sport `json:"-"`
而不是直接将其序列化为json,你需要先创建一个映射(基本上是一个视图模型),其中设置了固定属性。在这里,你可以设置SportID或Sport结构,然后将映射序列化为json。
这也不是一个很好的解决方案,但优点是在你当前的解决方案中,响应代码的丑陋泄漏到数据模型中,进而泄漏到应用程序的其他部分。通过中间映射,丑陋的部分被限制在响应生成中。
// models/league.go
type League struct {
Sport Sport
}
... 解析包含 ...
viewModel := map[string]interface{}
if includeSport {
viewModel["sport"] = league.Sport
} else {
viewModel["sport"] = league.Sport.ID
}
c.JSON(http.StatusOK, viewModel)
英文:
It's difficult to say without more context. I assume that the SportId is also part of the Sport struct. You are basically only returning the ID or the whole struct.
I would propose to only add the
Sport Sport `json:"-"`
struct. Instead of directly serializing it to json you would have to create a map (basically a viewmodel) first with the fixed attributes set. There you can set your SportID or Sport struct and after that you can serialize the map to json.
This is also not a very nice solution but the advantage is that in your current solution, the ugliness of your response code leaks into the data model and therefore into the rest of your application. With the intermediate map the ugly part is confined into the response generation.
// models/league.go
type League struct {
Sport Sport
}
... parse includes ...
viewModel := map[string]interface{}
if includeSport {
viewModel["sport"] = league.Sport
} else {
viewModel["sport"] = league.Sport.ID
}
c.JSON(http.StatusOK, viewModel)
答案2
得分: 0
// models/league.go
type League struct {
SportID int64 json:"-"
// 这里的json标签是用来指定在序列化和反序列化时的字段名
Sport Sport json:"-"
SportWrapper interface{} json:"sport"
}
var (
sportID int
sport Sport
)
// controllers/league.go
include := c.Request.URL.Query().Get("include")
if err := json.Unmarshal([]byte(include), &sportID); err != nil {
json.Unmarshal([]byte(include), &sport)
league.SportWrapper = sport
league.Sport = sport
} else {
league.SportWrapper = sportID
league.SportID = sportID
}
c.JSON(http.StatusOK, league)
如果你能将请求参数设置为 JSON 格式,你可以使用这段代码。
英文:
// models/league.go
type League struct {
SportID int64 `json:"-"`
Sport Sport `json:"-"`
SportWrapper interface{} `json:"sport"`
}
var (
sportID int
sport Sport
)
// controllers/league.go
include := c.Request.URL.Query().Get("include")
if err := json.Unmarshal([]byte(unclude), &sportID); err != nil {
json.Unmarshal([]byte(unclude), &sport)
league.SportWrapper = sport
league.Sport = sport
} else {
league.SportWrapper = sportID
league.SportID = sportID
}
c.JSON(http.StatusOK, league)
if you could set your request param to json you can use this.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论