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

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

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
[{
    &quot;id&quot;: &lt;event_id&gt;,
    &quot;name&quot;: &lt;event_name&gt;,
    &quot;team1&quot;: {
        &quot;id&quot;: &lt;team_id&gt;,
        &quot;name&quot;: &lt;team_name&gt;
    }
}]

// code
type Event struct {
    ID int64 `gorm:&quot;primary_key&quot; json:&quot;id&quot;`
    Team1ID int64 `json:&quot;-&quot;`
    Team1 Team `json:&quot;team1&quot;`
}

var event Event
Db.Preload(&quot;Team1&quot;).Find(&amp;event, 1)
c.JSON(http.StatusOK, event)

But we also want to be able to do this:

// request
GET /api/events
[{
    &quot;id&quot;: &lt;event_id&gt;,
    &quot;name&quot;: &lt;event_name&gt;,
    &quot;team1&quot;: &lt;team1_id&gt;
}]

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:&quot;primary_key&quot; json:&quot;id&quot;`
    Team1ID int64 `json:&quot;-&quot;`
    Team1 Team `json:&quot;team1&quot;`
}

type Team struct {
    ID int64 `gorm:&quot;primary_key&quot; json:&quot;id&quot;`
    Name string `json:&quot;name&quot;`
}

type EventInformations struct {
    ID int64 `json:&quot;id&quot;`
    Name string `json:&quot;name&quot;`
    Team1 `json&quot;team1&quot;`
}

type EventInformationsTeamIncluded struct {
    ID int64 `json:&quot;id&quot;`
    Name string `json:&quot;name&quot;`
    Team1 Team `json:&quot;team1&quot;`
}

func (e *Event) Informations() *EventInformations {
    return &amp;EventInformations{
         ID: e.ID,
         Name: e.Name,
         Team1: e.Team1,
    },
}

func (e *Event) InformationsTeamIncluded() *EventInformationsTeamIncluded {
    return &amp;EventInformations{
         ID: e.ID,
         Name: e.Name,
         Team1: &amp;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 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

func (e *Event) Informations() *map[string]interface{} {
    return &amp;map[string]interface{}{
         &quot;ID&quot;: e.ID,
         &quot;Name&quot;: e.Name,
         &quot;Team1&quot;: e.Team1,
    },
}

func (e *Event) InformationsTeamIncluded() *map[string]interface{} {
    // Reuse the previous function
    data := e.Informations()

    // Add the team1 informations
    data[&quot;team1&quot;] = 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:&quot;primary_key&quot; json:&quot;id&quot;`
    Team1ID int64 `json:&quot;-&quot;`
    Team1 interface{} `json:&quot;team1&quot;`
    Team1Struct Team `json:&quot;-&quot;`
}

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...)
}

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:

确定