一个结构体有多个JSON表示形式

huangapple go评论73阅读模式
英文:

One struct with multiple json representations

问题

我正在尝试解决的问题是,我有一个类似于以下结构的社区模型:

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:"-"语法,但我不确定如何根据情况进行操作,因为有时我仍然需要返回完整的对象。也许可以使用不同的类型进行转换?

英文:

The problem I'm trying to solve is that I have a model of a community that looks like this

type Community struct {
    Name string
    Description string
    Sources []Source
    Popularity int
    FavoriteCount int
    Moderators []string
    Children []Community
    Tracks []Track
}

Communities hold a lot of information and there are scenarios when I want to return only part of the description such as if I'm returning a list of trending communities. In this case I'd want to return only

type Community struct {
    Name string
    Description string
    Popularity int
    FavoriteCount int
}

The only way I can think of doing this is to create a new type containing only those fields and write a convenience method that takes a community and returns that type, but essentially creating a new object and copying those fields by value, is there a better way to do this?

I'm aware of the json:"-" syntax, but I'm not sure of how you could do this on a case by case basis as I still need to sometimes return the full object, perhaps a different type that is converted to?

答案1

得分: 23

这是一个很酷的方法,它涉及创建一种类似掩码的结构。

以下是文章中的示例:

type User struct {
    Email    string `json:"email"`
    Password string `json:"password"`
    // 还有很多其他字段...
}

type omit *struct{}

type PublicUser struct {
    *User
    Password omit `json:"password,omitempty"`
}

// 当你想要编码你的用户时:
json.Marshal(PublicUser{
    User: user,
})
英文:

This is a cool approach, which involves creating a sort of Masking struct.

Here's the example in the article:

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

答案2

得分: 5

是的,据我所知,使用默认的marshaler,这是唯一的方法。另外一个选项是创建自己的json.Marshaler。

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 {
 ...
}
英文:

Yep that is the only way as far as I know using the default marshaler. The only other option is if you create your own json.Marshaler .

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

得分: 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)
英文:

I developed a library which can help you in this regard: Sheriff

You can annotate your struct fields with special tags and call Sheriff to transform the given struct into a subset of it. After that you can call json.Marshal() or whatever else you want to marshal into.

Your example would become as simple as:

<!-- language: go -->

type Community struct {
    Name          string      `json:&quot;name&quot; groups:&quot;trending,detail&quot;`
    Description   string      `json:&quot;description&quot; groups:&quot;trending,detail&quot;`
    Sources       []Source    `json:&quot;sources&quot; groups:&quot;detail&quot;`
    Popularity    int         `json:&quot;popularity&quot; groups:&quot;trending,detail&quot;`
    FavoriteCount int         `json:&quot;favorite_count&quot; groups:&quot;trending,detail&quot;`
    Moderators    []string    `json:&quot;moderators&quot; groups:&quot;detail&quot;`
    Children      []Community `json:&quot;children&quot; groups:&quot;detail&quot;`
    Tracks        []Track     `json:&quot;tracks&quot; groups:&quot;detail&quot;`
}

communities := []Community{
    // communities
}

o := sheriff.Options{
    Groups: []string{&quot;trending&quot;},
}

d, err := sheriff.Marshal(&amp;o, communities)
if err != nil {
    panic(err)
}

out, _ := json.Marshal(d)

1: https://github.com/liip/sheriff "Sheriff"

答案4

得分: 4

我将为您提供另一种我开发的方法。我认为这种方法更加简洁。唯一的缺点是稍微复杂的对象初始化,但在使用中非常流畅。

主要的观点是,您不是基于原始对象构建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})
英文:

I'll present you another approach that I've developed. I think it's much more clean. The only downside is slightly complicated object initialization, but in usage it's very streamlined.

The main point is that you're not basing your JSON-view-object on the original object and then hiding elements in it, but the other way around, making it a part of the original object:

type CommunityBase struct {
Name string
Description string
}
type Community struct {
CommunityBase
FavoriteCount int
Moderators []string
}
var comm = Community{CommunityBase{&quot;Name&quot;, &quot;Descr&quot;}, 20, []string{&quot;Mod1&quot;,&quot;Mod2&quot;}}
json.Marshal(comm)
//{&quot;Name&quot;:&quot;Name&quot;,&quot;Description&quot;:&quot;Descr&quot;,&quot;FavoriteCount&quot;:20,&quot;Moderators&quot;:[&quot;Mod1&quot;,&quot;Mod2&quot;]}
json.Marshal(comm.CommunityBase)
//{&quot;Name&quot;:&quot;Name&quot;,&quot;Description&quot;:&quot;Descr&quot;}

And that's all if you need only one view, or if your views are gradually expanded.

But if your views can't be inherited, you'll have to resort to a kind of mixins, so you can make a combined view from them:

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{&quot;Bouncy Ball&quot;},
ThingVisualMixin: ThingVisualMixin{&quot;blue&quot;, true},
ThingTactileMixin: ThingTactileMixin{false},
Condition: &quot;Good&quot;,
}
obj.visualView = &amp;ThingVisualView{&amp;obj.ThingBaseMixin, &amp;obj.ThingVisualMixin}
obj.tactileView = &amp;ThingTactileView{&amp;obj.ThingBaseMixin, &amp;obj.ThingTactileMixin}
b, _ := json.Marshal(obj)
fmt.Println(string(b))
//{&quot;Name&quot;:&quot;Bouncy Ball&quot;,&quot;Color&quot;:&quot;blue&quot;,&quot;IsRound&quot;:true,&quot;IsSoft&quot;:false,&quot;Condition&quot;:&quot;Good&quot;}
b, _ = json.Marshal(obj.ThingVisualMixin)
fmt.Println(string(b))
//{&quot;Color&quot;:&quot;blue&quot;,&quot;IsRound&quot;:true}
b, _ = json.Marshal(obj.visualView)
fmt.Println(string(b))
//{&quot;Name&quot;:&quot;Bouncy Ball&quot;,&quot;Color&quot;:&quot;blue&quot;,&quot;IsRound&quot;:true}
b, _ = json.Marshal(obj.tactileView)
fmt.Println(string(b))
//{&quot;Name&quot;:&quot;Bouncy Ball&quot;,&quot;IsSoft&quot;:false}
}

Here I've added a view into the object, but if you like, you can create it just when calling Marshal:

json.Marshal(ThingVisualView{&amp;obj.ThingBaseMixin, &amp;obj.ThingVisualMixin})

Or even without a preliminary type declaration:

json.Marshal(struct{*ThingBaseMixin;*ThingVisualMixin}{&amp;obj.ThingBaseMixin,&amp;obj.ThingVisualMixin})

答案5

得分: 0

不确定为什么这不是首选的方法,可能是因为帖子的年代,但据我所知,这是处理此问题的“最佳实践”方式,对于那些在JSON对象中不必存在的字段,可以使用“omitempty”标签。

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"`
}
英文:

Not sure why this isn't the preferred method, maybe due to the age of the post, but as far as I know, this is the 'best practice' way to handle this, with 'omitempty' tags for those which don't have to exist in the JSON object.

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

huangapple
  • 本文由 发表于 2014年7月16日 08:19:42
  • 转载请务必保留本文链接:https://go.coder-hub.com/24770235.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定