Одна структура с несколькими json-представлениями в Голанге
Проблема, которую я пытаюсь решить, заключается в том, что у меня есть модель сообщества, которая выглядит так:
type Community struct {
Name string
Description string
Sources []Source
Popularity int
FavoriteCount int
Moderators []string
Children []Community
Tracks []Track
}
Сообщества содержат много информации, и есть сценарии, когда я хочу вернуть только часть описания, например, если я возвращаю список тредных сообществ. В этом случае я бы хотел вернуться только
type Community struct {
Name string
Description string
Popularity int
FavoriteCount int
}
Единственный способ, которым я могу это сделать, - создать новый тип, содержащий только те поля, и написать метод удобства, который берет сообщество и возвращает этот тип, но по существу создавая новый объект и копируя эти поля по значению, является есть лучший способ сделать это?
Я знаю синтаксис json:"-"
, но я не уверен, как вы могли бы это сделать в каждом конкретном случае, поскольку мне все же нужно иногда возвращать полный объект, возможно, другой тип, который является typecasted к?
Ответы
Ответ 1
Я разработал библиотеку, которая поможет вам в этом отношении: Sheriff
Вы можете аннотировать ваши поля структуры специальными тегами и вызывать шериф, чтобы преобразовать данную структуру в ее подмножество. После этого вы можете вызвать json.Marshal()
или все, что вы хотите включить.
Ваш пример станет таким же простым, как:
type Community struct {
Name string `json:"name" groups:"trending,detail"`
Description string `json:"description" groups:"trending,detail"`
Sources []Source `json:"sources" groups:"detail"`
Popularity int `json:"popularity" groups:"trending,detail"`
FavoriteCount int `json:"favorite_count" groups:"trending,detail"`
Moderators []string `json:"moderators" groups:"detail"`
Children []Community `json:"children" groups:"detail"`
Tracks []Track `json:"tracks" groups:"detail"`
}
communities := []Community{
// communities
}
o := sheriff.Options{
Groups: []string{"trending"},
}
d, err := sheriff.Marshal(&o, communities)
if err != nil {
panic(err)
}
out, _ := json.Marshal(d)
Ответ 2
Редактирование моего ответа - нашел лучший способ:
Это крутой подход:
http://attilaolah.eu/2014/09/10/json-and-struct-composition-in-go/
Вовлекает создание своего рода структуры маскирования.
Вот пример в статье:
type User struct {
Email string `json:"email"`
Password string `json:"password"`
// many more fields…
}
type omit *struct{}
type PublicUser struct {
*User
Password omit `json:"password,omitempty"`
}
// when you want to encode your user:
json.Marshal(PublicUser{
User: user,
})
Ответ 3
Да, это единственный способ, насколько я знаю, используя Marshaller по умолчанию. Единственный другой вариант - создать собственный JsonMarshaller.
type Community struct {
}
type CommunityShort Community
func (key *Community) MarshalJSON() ([]byte, os.Error) {
...
}
func (key *Community) UnmarshalJSON(data []byte) os.Error {
...
}
func (key *CommunityShort) MarshalJSON() ([]byte, os.Error) {
...
}
func (key *CommunityShort) UnmarshalJSON(data []byte) os.Error {
...
}
Ответ 4
Я представлю вам другой подход, который я разработал. Я думаю, что это намного чище. Единственным недостатком является немного сложная инициализация объекта, но в использовании он очень обтекаемый.
Главное, что вы не основываете свой объект JSON-view на исходном объекте и затем скрываете в нем элементы, но наоборот, делая его частью исходного объекта:
type CommunityBase struct {
Name string
Description string
}
type Community struct {
CommunityBase
FavoriteCount int
Moderators []string
}
var comm = Community{CommunityBase{"Name", "Descr"}, 20, []string{"Mod1","Mod2"}}
json.Marshal(comm)
//{"Name":"Name","Description":"Descr","FavoriteCount":20,"Moderators":["Mod1","Mod2"]}
json.Marshal(comm.CommunityBase)
//{"Name":"Name","Description":"Descr"}
И это все, если вам нужно только одно представление, или если ваши взгляды постепенно расширяются.
Но если ваши представления не могут быть унаследованы, вам придется прибегать к каким-то миксинам, поэтому вы можете сделать из них комбинированный вид:
type ThingBaseMixin struct {
Name string
}
type ThingVisualMixin struct {
Color string
IsRound bool
}
type ThingTactileMixin struct {
IsSoft bool
}
type Thing struct {
ThingBaseMixin
ThingVisualMixin
ThingTactileMixin
Condition string
visualView *ThingVisualView
tactileView *ThingTactileView
}
type ThingVisualView struct {
*ThingBaseMixin
*ThingVisualMixin
}
type ThingTactileView struct {
*ThingBaseMixin
*ThingTactileMixin
}
func main() {
obj := Thing {
ThingBaseMixin: ThingBaseMixin{"Bouncy Ball"},
ThingVisualMixin: ThingVisualMixin{"blue", true},
ThingTactileMixin: ThingTactileMixin{false},
Condition: "Good",
}
obj.visualView = &ThingVisualView{&obj.ThingBaseMixin, &obj.ThingVisualMixin}
obj.tactileView = &ThingTactileView{&obj.ThingBaseMixin, &obj.ThingTactileMixin}
b, _ := json.Marshal(obj)
fmt.Println(string(b))
//{"Name":"Bouncy Ball","Color":"blue","IsRound":true,"IsSoft":false,"Condition":"Good"}
b, _ = json.Marshal(obj.ThingVisualMixin)
fmt.Println(string(b))
//{"Color":"blue","IsRound":true}
b, _ = json.Marshal(obj.visualView)
fmt.Println(string(b))
//{"Name":"Bouncy Ball","Color":"blue","IsRound":true}
b, _ = json.Marshal(obj.tactileView)
fmt.Println(string(b))
//{"Name":"Bouncy Ball","IsSoft":false}
}
Здесь я добавил представление в объект, но если хотите, вы можете создать его только при вызове Marshal
:
json.Marshal(ThingVisualView{&obj.ThingBaseMixin, &obj.ThingVisualMixin})
Или даже без предварительного объявления типа:
json.Marshal(struct{*ThingBaseMixin;*ThingVisualMixin}{&obj.ThingBaseMixin,&obj.ThingVisualMixin})