Есть несколько способов добиться этого.Первым было бы создать собственный маршаллер для типа Table
.Это, однако, несколько утомительно и может быть довольно ограничительным.ИМХО, есть более простой способ сделать то же самое: embed types:
type PartialPlayer struct {
Player // embed the entire type
Email string `json:"-"` // override fields and add the tag to exclude them
Birthdate string `json:"-"`
}
Теперь вы можете получить доступ ко всем нужным данным и даже добавить геттеры для косвенного доступа к данным:
func (pp PartialPlayer) GetEmail() string {
if pp.Email == "" {
return pp.Player.Email // get embedded Email value
}
return pp.Email // add override value
}
Обратите внимание, что вам не нужно , чтобы использовать эти функции получения.Поле Id
не переопределяется, поэтому, если у меня есть переменная PartialPlayer
, я могу получить доступ к значению напрямую:
pp := PartialPlayer{
Player: playerVar,
}
fmt.Printf("Player ID: %v\n", pp.Id) // still works
Вы можете получить доступ к переопределенным / замаскированным полям, указав, что вы хотите, чтобы значение удерживалосьвстроенный тип, также без функции:
fmt.Printf("Email on partial: '%s', but I can see '%s'\n", pp.Email, pp.Player.Email)
Последний напечатает Email on partial: '', but I can see 'foo@bar.com'
.
Используйте этот тип в Table
, например:
type Table struct {
Id int `json:"id"`
PlayerTop PartialPlayer `json:"playerTop"`
PlayerBottom PartialPlayer `json:"playerBottom"`
}
Инициализация:
tbl := Table{
Id: 213,
PlayerTop: PartialPlayer{
Player: playerVar,
},
PlayerBottom: PartialPlayer{
Player: player2Var,
},
}
Это прекрасно работает.Преимущество этого подхода состоит в том, что для маршалинга в JSON и из него не требуется вызов ваших пользовательских функций маршаллера, а также создание / отображение промежуточных типов, таких как карты или скрытые типы и т. Д. *
Если вы хотите скрытьдругое поле, просто добавьте его к типу PartialPlayer
.Если вы хотите отобразить поле типа Email
, просто удалите его из типа PartialPlayer
, работа выполнена.
Теперь для подхода с пользовательским маршаллером:
type Table struct {
Id int `json:"id"`
PlayerTop Player `json:"playerTop"`
PlayerBottom Player `json:"playerBottom"`
}
type marshalTable {
Table
// assuming the PartialPlayer type above
PlayerTop PartialPlayer `json:"playerTop"`
PlayerBottom PartialPlayer `json:"playerBottom"`
}
func (t Table) MarshalJSON() ([]byte, error) {
mt := marshalTable{
Table: t,
PlayerTop: PartialPlayer{
Player: t.PlayerTop,
},
PlayerBottom: PartialPlayer{
Player: t.PlayerBottom,
},
}
return json.Marshal(mt)
}
Это не слишком отличается от построения типа map[string]interface{}
здесь, но при использовании встраивания типа вам не нужно обновлять функцию маршаллера каждый раз, когда поле переименовывается или изменяется в типе Player
.
Используя этот подход, ваш тип Table
может использоваться точно так же, как вы делаете это сейчас, но вывод JSON не будет включать поля Email
и Birthdate
.