英文:
How to modify fields of a Golang struct to another type before rendering to jSON?
问题
我们正在我们的API中添加一个包含参数,API客户端可以使用它来包含关联关系。
// 请求
GET /api/events?include=team1
[{
"id": <event_id>,
"name": <event_name>,
"team1": {
"id": <team_id>,
"name": <team_name>
}
}]
// 代码
type Event struct {
ID int64 gorm:"primary_key" json:"id"
Team1ID int64 json:"-"
Team1 Team json:"team1"
}
var event Event
Db.Preload("Team1").Find(&event, 1)
c.JSON(http.StatusOK, event)
但我们还想能够这样做:
// 请求
GET /api/events
[{
"id": <event_id>,
"name": <event_name>,
"team1": <team1_id>
}]
现在,team1字段只是一个id。
在Go中有没有简单的方法可以做到这一点?
我认为我可以使用map[string]interface{}
来实现这一点,比如在从数据库中获取事件之后,将事件结构转换为map[string]interface{}
并进行修改。但我想知道是否有更简单的解决方案。
这是我尝试使用map[string]interface{}
的代码-https://play.golang.org/p/-19MWtqhE3。代码非常冗长。我的想法是为每个结构使用map[string]interface{}
,然后组合包含相关资源的顶级资源。
有更好的方法吗?
英文:
We are adding an include parameter in our API w/c API clients can use to include relationships
// request
GET /api/events?include=team1
[{
"id": <event_id>,
"name": <event_name>,
"team1": {
"id": <team_id>,
"name": <team_name>
}
}]
// code
type Event struct {
ID int64 `gorm:"primary_key" json:"id"`
Team1ID int64 `json:"-"`
Team1 Team `json:"team1"`
}
var event Event
Db.Preload("Team1").Find(&event, 1)
c.JSON(http.StatusOK, event)
But we also want to be able to do this:
// request
GET /api/events
[{
"id": <event_id>,
"name": <event_name>,
"team1": <team1_id>
}]
The team1 field is now just an id.
Is there an easy way to do this in Go?
I think I can do this by using a map[string]interface{}
, like after fetching the events in the db, convert the event structs to a map[string]interface{}
and do the modifications. But I'm wondering if theres an easier solution.
Here's my attempt in using map[string]interface{}
- https://play.golang.org/p/-19MWtqhE3 . The code is very verbose. The idea is to use map[string]interface{}
for every struct then compose the top level resource that includes the related resources.
What's a better way to do this?
答案1
得分: 1
我认为最清晰的方法是使用两个不同的函数。
type Event struct {
ID int64 `gorm:"primary_key" json:"id"`
Team1ID int64 `json:"-"`
Team1 Team `json:"team1"`
}
type Team struct {
ID int64 `gorm:"primary_key" json:"id"`
Name string `json:"name"`
}
type EventInformations struct {
ID int64 `json:"id"`
Name string `json:"name"`
Team1 `json"team1"`
}
type EventInformationsTeamIncluded struct {
ID int64 `json:"id"`
Name string `json:"name"`
Team1 Team `json:"team1"`
}
func (e *Event) Informations() *EventInformations {
return &EventInformations{
ID: e.ID,
Name: e.Name,
Team1: e.Team1,
}
}
func (e *Event) InformationsTeamIncluded() *EventInformationsTeamIncluded {
return &EventInformations{
ID: e.ID,
Name: e.Name,
Team1: &Team{
...
},
}
}
稍后只需调用:
event.Informations();
或者
event.InformationsTeamIncluded();
你的代码/示例中有一些不太合理的地方:
- 变量
Team1
有点奇怪,因为结构体中只有一个值,所以如果你不打算在结构体中添加一些Team
,我建议将其替换为Team
;如果你计划有多个Team
,则将其替换为Teams []*Team
。 - 第二个问题是在你的示例中,你向同一个名称返回了两个不同的值,这取决于上下文。我建议在某种情况下返回
team1_id
而不是team1
。
编辑:不使用结构体的情况下:
func (e *Event) Informations() *map[string]interface{} {
return &map[string]interface{}{
"ID": e.ID,
"Name": e.Name,
"Team1": e.Team1,
}
}
func (e *Event) InformationsTeamIncluded() *map[string]interface{} {
// 重用之前的函数
data := e.Informations()
// 添加team1的信息
data["team1"] = map[string]interface{}{
"ID": e.Team1.ID,
"Name": e.Team1.Name,
}
return data
}
英文:
I think the most cleaner way is to use two different functions.
type Event struct {
ID int64 `gorm:"primary_key" json:"id"`
Team1ID int64 `json:"-"`
Team1 Team `json:"team1"`
}
type Team struct {
ID int64 `gorm:"primary_key" json:"id"`
Name string `json:"name"`
}
type EventInformations struct {
ID int64 `json:"id"`
Name string `json:"name"`
Team1 `json"team1"`
}
type EventInformationsTeamIncluded struct {
ID int64 `json:"id"`
Name string `json:"name"`
Team1 Team `json:"team1"`
}
func (e *Event) Informations() *EventInformations {
return &EventInformations{
ID: e.ID,
Name: e.Name,
Team1: e.Team1,
},
}
func (e *Event) InformationsTeamIncluded() *EventInformationsTeamIncluded {
return &EventInformations{
ID: e.ID,
Name: e.Name,
Team1: &Team{
...
},
}
}
So later just call
event.Informations();
Or
event.InformationsTeamIncluded();
There is something not very logical in your code/examples:
- The var
Team1
is a little bit weird cause there is only one value in the struct so if you don't have plan to add someTeam
in struct I suggest to replace it by Team if you plan to have any Team replace it byTeams []*Team
- The second thing is in your example your return two differents values to a same name, it depends of the context I suggest in one case to return
team1_id
instead ofteam1
EDIT : without struct
func (e *Event) Informations() *map[string]interface{} {
return &map[string]interface{}{
"ID": e.ID,
"Name": e.Name,
"Team1": e.Team1,
},
}
func (e *Event) InformationsTeamIncluded() *map[string]interface{} {
// Reuse the previous function
data := e.Informations()
// Add the team1 informations
data["team1"] = map[string]interface{}{
ID: e.Team1.ID,
Name: e.Team1.Name,
}
return data
}
答案2
得分: 1
实际上,你可以将team1设置为一个接口,然后转换其值,当然要进行适当的验证。
type Event struct {
ID int64 `gorm:"primary_key" json:"id"`
Team1ID int64 `json:"-"`
Team1 interface{} `json:"team1"`
Team1Struct Team `json:"-"`
}
然后进行评估:
if value, ok := evt.Team1.(Team); ok {
// 传递的值是一个Team结构体
event.Team1Struct = value
} else {
// 传递的值是一个字符串(或其他类型...)
}
希望对你有所帮助!
英文:
Actually you could just set the team1 to be an interface and then cast it's value, obviously making the proper validations.
type Event struct {
ID int64 `gorm:"primary_key" json:"id"`
Team1ID int64 `json:"-"`
Team1 interface{} `json:"team1"`
Team1Struct Team `json:"-"`
}
And then evaluate:
if value, ok := evt.Team1.(Team); ok {
// The passed value is a Team struct
event.Team1Struct = value
} else {
// The passed value is a string (or something else...)
}
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论