John Kenn
John Kenn

Reputation: 1665

How to modify fields of a Golang struct to another type before rendering to jSON?

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?

Upvotes: 1

Views: 1822

Answers (2)

Ricardo Gamba
Ricardo Gamba

Reputation: 164

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

Upvotes: 1

Manawasp
Manawasp

Reputation: 557

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 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 &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
}

Upvotes: 1

Related Questions