如何在渲染为JSON之前将Golang结构体的字段修改为另一种类型?

huangapple go评论108阅读模式
英文:

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

  1. // request
  2. GET /api/events?include=team1
  3. [{
  4. &quot;id&quot;: &lt;event_id&gt;,
  5. &quot;name&quot;: &lt;event_name&gt;,
  6. &quot;team1&quot;: {
  7. &quot;id&quot;: &lt;team_id&gt;,
  8. &quot;name&quot;: &lt;team_name&gt;
  9. }
  10. }]
  11. // code
  12. type Event struct {
  13. ID int64 `gorm:&quot;primary_key&quot; json:&quot;id&quot;`
  14. Team1ID int64 `json:&quot;-&quot;`
  15. Team1 Team `json:&quot;team1&quot;`
  16. }
  17. var event Event
  18. Db.Preload(&quot;Team1&quot;).Find(&amp;event, 1)
  19. c.JSON(http.StatusOK, event)

But we also want to be able to do this:

  1. // request
  2. GET /api/events
  3. [{
  4. &quot;id&quot;: &lt;event_id&gt;,
  5. &quot;name&quot;: &lt;event_name&gt;,
  6. &quot;team1&quot;: &lt;team1_id&gt;
  7. }]

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

我认为最清晰的方法是使用两个不同的函数。

  1. type Event struct {
  2. ID int64 `gorm:"primary_key" json:"id"`
  3. Team1ID int64 `json:"-"`
  4. Team1 Team `json:"team1"`
  5. }
  6. type Team struct {
  7. ID int64 `gorm:"primary_key" json:"id"`
  8. Name string `json:"name"`
  9. }
  10. type EventInformations struct {
  11. ID int64 `json:"id"`
  12. Name string `json:"name"`
  13. Team1 `json"team1"`
  14. }
  15. type EventInformationsTeamIncluded struct {
  16. ID int64 `json:"id"`
  17. Name string `json:"name"`
  18. Team1 Team `json:"team1"`
  19. }
  20. func (e *Event) Informations() *EventInformations {
  21. return &EventInformations{
  22. ID: e.ID,
  23. Name: e.Name,
  24. Team1: e.Team1,
  25. }
  26. }
  27. func (e *Event) InformationsTeamIncluded() *EventInformationsTeamIncluded {
  28. return &EventInformations{
  29. ID: e.ID,
  30. Name: e.Name,
  31. Team1: &Team{
  32. ...
  33. },
  34. }
  35. }

稍后只需调用:

  1. event.Informations();

或者

  1. event.InformationsTeamIncluded();

你的代码/示例中有一些不太合理的地方:

  • 变量Team1有点奇怪,因为结构体中只有一个值,所以如果你不打算在结构体中添加一些Team,我建议将其替换为Team;如果你计划有多个Team,则将其替换为Teams []*Team
  • 第二个问题是在你的示例中,你向同一个名称返回了两个不同的值,这取决于上下文。我建议在某种情况下返回team1_id而不是team1

编辑:不使用结构体的情况下:

  1. func (e *Event) Informations() *map[string]interface{} {
  2. return &map[string]interface{}{
  3. "ID": e.ID,
  4. "Name": e.Name,
  5. "Team1": e.Team1,
  6. }
  7. }
  8. func (e *Event) InformationsTeamIncluded() *map[string]interface{} {
  9. // 重用之前的函数
  10. data := e.Informations()
  11. // 添加team1的信息
  12. data["team1"] = map[string]interface{}{
  13. "ID": e.Team1.ID,
  14. "Name": e.Team1.Name,
  15. }
  16. return data
  17. }
英文:

I think the most cleaner way is to use two different functions.

  1. type Event struct {
  2. ID int64 `gorm:&quot;primary_key&quot; json:&quot;id&quot;`
  3. Team1ID int64 `json:&quot;-&quot;`
  4. Team1 Team `json:&quot;team1&quot;`
  5. }
  6. type Team struct {
  7. ID int64 `gorm:&quot;primary_key&quot; json:&quot;id&quot;`
  8. Name string `json:&quot;name&quot;`
  9. }
  10. type EventInformations struct {
  11. ID int64 `json:&quot;id&quot;`
  12. Name string `json:&quot;name&quot;`
  13. Team1 `json&quot;team1&quot;`
  14. }
  15. type EventInformationsTeamIncluded struct {
  16. ID int64 `json:&quot;id&quot;`
  17. Name string `json:&quot;name&quot;`
  18. Team1 Team `json:&quot;team1&quot;`
  19. }
  20. func (e *Event) Informations() *EventInformations {
  21. return &amp;EventInformations{
  22. ID: e.ID,
  23. Name: e.Name,
  24. Team1: e.Team1,
  25. },
  26. }
  27. func (e *Event) InformationsTeamIncluded() *EventInformationsTeamIncluded {
  28. return &amp;EventInformations{
  29. ID: e.ID,
  30. Name: e.Name,
  31. Team1: &amp;Team{
  32. ...
  33. },
  34. }
  35. }

So later just call

  1. event.Informations();

Or

  1. 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 some Team in struct I suggest to replace it by Team if you plan to have any Team replace it by Teams []*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 of team1

EDIT : without struct

  1. func (e *Event) Informations() *map[string]interface{} {
  2. return &amp;map[string]interface{}{
  3. &quot;ID&quot;: e.ID,
  4. &quot;Name&quot;: e.Name,
  5. &quot;Team1&quot;: e.Team1,
  6. },
  7. }
  8. func (e *Event) InformationsTeamIncluded() *map[string]interface{} {
  9. // Reuse the previous function
  10. data := e.Informations()
  11. // Add the team1 informations
  12. data[&quot;team1&quot;] = map[string]interface{}{
  13. ID: e.Team1.ID,
  14. Name: e.Team1.Name,
  15. }
  16. return data
  17. }

答案2

得分: 1

实际上,你可以将team1设置为一个接口,然后转换其值,当然要进行适当的验证。

  1. type Event struct {
  2. ID int64 `gorm:"primary_key" json:"id"`
  3. Team1ID int64 `json:"-"`
  4. Team1 interface{} `json:"team1"`
  5. Team1Struct Team `json:"-"`
  6. }

然后进行评估:

  1. if value, ok := evt.Team1.(Team); ok {
  2. // 传递的值是一个Team结构体
  3. event.Team1Struct = value
  4. } else {
  5. // 传递的值是一个字符串(或其他类型...)
  6. }

希望对你有所帮助!

英文:

Actually you could just set the team1 to be an interface and then cast it's value, obviously making the proper validations.

  1. type Event struct {
  2. ID int64 `gorm:&quot;primary_key&quot; json:&quot;id&quot;`
  3. Team1ID int64 `json:&quot;-&quot;`
  4. Team1 interface{} `json:&quot;team1&quot;`
  5. Team1Struct Team `json:&quot;-&quot;`
  6. }

And then evaluate:

  1. if value, ok := evt.Team1.(Team); ok {
  2. // The passed value is a Team struct
  3. event.Team1Struct = value
  4. } else {
  5. // The passed value is a string (or something else...)
  6. }

huangapple
  • 本文由 发表于 2016年4月29日 16:35:39
  • 转载请务必保留本文链接:https://go.coder-hub.com/36933487.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定