Одна структура с несколькими 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})