一个在Golang中具有多个json表示的结构

时间:2014-07-16 00:19:43

标签: json go

我试图解决的问题是我有一个类似于此的社区模型

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:"-"语法,但我不确定你是如何根据具体情况做到这一点的,因为我仍然需要有时返回完整的对象,也许是一种类型不同的类型?

5 个答案:

答案 0 :(得分:15)

编辑我的答案 - 找到了更好的方法:

这是一个很酷的方法:

http://attilaolah.eu/2014/09/10/json-and-struct-composition-in-go/

涉及创建一种Masking结构。

以下是文章中的示例:

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

答案 1 :(得分:5)

我开发了一个可以在这方面为您提供帮助的库:Sheriff

您可以使用特殊标记注释结构字段,并调用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 :(得分:4)

是的,就我所知,使用默认的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 {
 ...
}

答案 3 :(得分:3)

我将向您介绍我开发的另一种方法。我认为它更干净。唯一的缺点是稍微复杂的对象初始化,但在使用中它非常简化。

主要的一点是,您不是将JSON视图对象基于原始对象,然后将元素隐藏在其中,反之亦然,使其成为原始对象的一部分:

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

答案 4 :(得分:0)

不确定为什么这不是首选方法,可能是由于帖子的年龄,但据我所知,这是处理此问题的“最佳实践”方式,对于那些带有“省略”标签的人不必存在于 JSON 对象中。

type Community struct {
    Name          string       `json:"name"`
    Description   string       `json:"description"` 
    Sources       *[]Source    `json:"sources,omitempty"`
    Popularity    int          `json:"popularity"`
    FavoriteCount int          `json:"favorite-count"`
    Moderators    *[]string    `json:"moderators,omitempty"`
    Children      *[]Community `json:"children,omitempty"`
    Tracks        *[]Track     `json:"tracks,omitempty"`
}