How to skip JSON fields in Struct on input and display them on output, and accept certain fields in input and skip them in output in Golang?

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

How to skip JSON fields in Struct on input and display them on output, and accept certain fields in input and skip them in output in Golang?

问题

我正在使用Echo框架和Gorm编写一个Go语言的Web服务。
我有一个名为User的结构体,如下所示:

type User struct {
    ID    uint   `json:"user_id"`
    Email string `json:"email_address,omitempty" validate:"required,email"`
}

我接受POST请求,请求的Content-typeapplication/json
我希望我的输入是{"email_address": "email@email.com"},输出是{"user_id": 1}

如何禁止用户在请求中提交ID(以防止他们创建具有特定ID的记录),但保留响应中的ID呢?
目前我在保存之前执行user.ID = 0,但我想知道是否有更好的方法?

我还想在输出中跳过Email字段。目前我在输出之前执行user.Email = "",是否有更好的方法?

谢谢!

英文:

I'm writing a webservice in Go using Echo framework and Gorm.
I have a User struct that looks like this:

type User struct {
    ID    uint   `json:"user_id"`
    Email string `json:"email_address,omitempty" validate:"required,email"`
}

I'm accepting POST requests with a Content-type: application/json.
I want my input to be {"email_address": "email@email.com"} and my output to be {"user_id": 1}

How can I forbid users from submitting ID in the request (so they can't create records with certain IDs), but keep ID in the response?
Right now I'm doing user.ID = 0 right before save, but I wonder if there's a better way to do it?

I also want to skip Email in output. Right now I'm doing user.Email = "" right before the output. Is there a better way for that also?

Thanks!

答案1

得分: 3

icza的答案提供了一个不错的解决方案,你也可以使用JSON编组辅助方法MarshalJSON/UnmarshalJSON

func main() {
    // 使用辅助的json方法MarshalJSON/UnmarshalJSON
    user := User{ID: 123, Email: `abc@xyz.com`}
    js, _ := json.Marshal(&user)
    log.Printf("%s", js)

    input := UserInput(user)
    js, _ = json.Marshal(&input)
    log.Printf("%s", js)

    output := UserOutput(user)
    js, _ = json.Marshal(&output)
    log.Printf("%s", js)
}

type User struct {
    ID    uint   `json:"user_id"`
    Email string `json:"email_address,omitempty" validate:"required,email"`
}

type UserInput User

func (x *UserInput) MarshalJSON() ([]byte, error) {
    return json.Marshal(&struct {
        Email string `json:"email_address,omitempty" validate:"required,email"`
    }{
        Email: x.Email,
    })
}

type UserOutput User

func (x *UserOutput) MarshalJSON() ([]byte, error) {
    return json.Marshal(&struct {
        ID uint `json:"user_id"`
    }{
        ID: x.ID,
    })
}

这将给出以下结果:

[info] main.go:25: {"user_id":123,"email_address":"abc@xyz.com"}
[info] main.go:29: {"email_address":"abc@xyz.com"}
[info] main.go:33: {"user_id":123}

Go Playground上查看。

英文:

While icza's answer proposes a nice solution, you could also employ JSON marshaling auxiliary methods MarshalJSON/UnmarshalJSON:

func main() {
	// employing auxiliary json methods MarshalJSON/UnmarshalJSON
	user := User{ID: 123, Email: `abc@xyz.com`}
	js, _ := json.Marshal(&user)
	log.Printf("%s", js)

	input := UserInput(user)
	js, _ = json.Marshal(&input)
	log.Printf("%s", js)

	output := UserOutput(user)
	js, _ = json.Marshal(&output)
	log.Printf("%s", js)
}

type User struct {
	ID    uint   `json:"user_id"`
	Email string `json:"email_address,omitempty" validate:"required,email"`
}

type UserInput User

func (x *UserInput) MarshalJSON() ([]byte, error) {
	return json.Marshal(&struct {
		Email string `json:"email_address,omitempty" validate:"required,email"`
	}{
		Email: x.Email,
	})
}

type UserOutput User

func (x *UserOutput) MarshalJSON() ([]byte, error) {
	return json.Marshal(&struct {
		ID uint `json:"user_id"`
	}{
		ID: x.ID,
	})
}

Which gives us:

[  info ] main.go:25: {"user_id":123,"email_address":"abc@xyz.com"}
[  info ] main.go:29: {"email_address":"abc@xyz.com"}
[  info ] main.go:33: {"user_id":123}

On Go Playground.

答案2

得分: 2

我通过将示例更加通用化,并展示了更一般问题的优雅解决方案来扩展了你的示例。

假设在User中有以下情况:

  • 一些字段只能从输入中解析
  • 一些字段只能出现在输出中
  • 还有一些字段既需要从输入中解析,也需要出现在输出中

你的示例是这个问题的一个"子集"(因为在你的示例中没有共同的字段)。

可以通过使用嵌入来优雅地解决这个问题,而无需重复字段。你可以创建3个单独的类型:一个用于共同字段UserBoth,一个用于仅在输入中的字段UserIn,一个用于仅在输出中的字段UserOut

type UserBoth struct {
    Name string `json:"name"`
    Age  int    `json:"age"`
}

type UserIn struct {
    *UserBoth
    Email string `json:"email"`
}

type UserOut struct {
    *UserBoth
    ID uint `json:"id"`
}

请注意,UserBoth(或者更准确地说是*UserBoth,以避免重复结构值)在UserInUserOut中都被嵌入,因为我们希望这些字段都出现在两者中。

现在,如果你有一个包含所有字段的输入(即使我们不想要所有字段):

const in = `{"name":"Bob","age":23,"Email":"as@as.com","id":123}`

将其解组为UserIn的值将只解析你想要的内容:

uin := UserIn{UserBoth: &UserBoth{}}
err := json.Unmarshal([]byte(in), &uin)
fmt.Printf("%+v %+v %v\n", uin, uin.UserBoth, err)

输出结果(注意Email字段被解析,但ID字段没有):

{UserBoth:0x1050e150 Email:as@as.com} &{Name:Bob Age:23} <nil>

当你想要生成输出时:

uout := UserOut{UserBoth: uin.UserBoth}
// 从数据库获取/设置ID:
uout.ID = 456
out, err := json.Marshal(uout)
fmt.Println(string(out), err)

输出结果(注意ID字段存在,但Email字段不存在):

{"name":"Bob","age":23,"id":456} <nil>

你可以在Go Playground上尝试一下。

使用"统一"的User

上面的示例使用了两个不同的结构体:UserIn用于解析,UserOut用于生成输出。

如果在你的代码中你想要使用一个"统一"的User结构体,可以这样做:

type User struct {
    UserIn
    UserOut
}

使用它:

uboth := &UserBoth{}
u := User{UserIn{UserBoth: uboth}, UserOut{UserBoth: uboth}}
err := json.Unmarshal([]byte(in), &u.UserIn)
fmt.Printf("%+v %+v %v\n", u, u.UserIn.UserBoth, err)

// 从数据库获取/设置ID:
u.ID = 456
out, err := json.Marshal(u.UserOut)
fmt.Println(string(out), err)

你可以在Go Playground上尝试这个示例。

英文:

I extend your example by making it more general, and I show an elegant solution to the more general problem.

Let's assume that in User there are:

  • some fields that must be parsed only from the input
  • some fields that must appear only in the output
  • and there are some fields that must be parsed from input and must also appear in output

Your example is a "subset" of this (as in your example there are no common fields).

This can be elegantly solved without repeating fields using embedding. You may create 3 separate types; one for the common fields UserBoth, one for the input-only fields UserIn, and one for the output-only fields UserOut:

type UserBoth struct {
	Name string `json:&quot;name&quot;`
	Age  int    `json:&quot;age&quot;`
}

type UserIn struct {
	*UserBoth
	Email string `json:&quot;email&quot;`
}

type UserOut struct {
	*UserBoth
	ID uint `json:&quot;id&quot;`
}

Note that UserBoth (or rather *UserBoth to avoid duplicating the struct value) is embedded in both UserIn and UserOut, because we want those fields in both.

Now if you have an input that contains all fields (even though we don't want all):

const in = `{&quot;name&quot;:&quot;Bob&quot;,&quot;age&quot;:23,&quot;Email&quot;:&quot;as@as.com&quot;,&quot;id&quot;:123}`

Unmarshaling into a value of UserIn will only parse what you want:

uin := UserIn{UserBoth: &amp;UserBoth{}}
err := json.Unmarshal([]byte(in), &amp;uin)
fmt.Printf(&quot;%+v %+v %v\n&quot;, uin, uin.UserBoth, err)

Output (note that the Email field is parsed but ID isn't):

{UserBoth:0x1050e150 Email:as@as.com} &amp;{Name:Bob Age:23} &lt;nil&gt;

And when you want to generate output:

uout := UserOut{UserBoth: uin.UserBoth}
// Fetch / set ID from db:
uout.ID = 456
out, err := json.Marshal(uout)
fmt.Println(string(out), err)

Output (note that the ID field is present but Email isn't):

{&quot;name&quot;:&quot;Bob&quot;,&quot;age&quot;:23,&quot;id&quot;:456} &lt;nil&gt;

Try it on the Go Playground.

Having a "unified" User

The above example used 2 different structs: UserIn for parsing and UserOut to generate the output.

If inside your code you want to use a "unified" User struct, this is how it can be done:

type User struct {
	UserIn
	UserOut
}

Using it:

uboth := &amp;UserBoth{}
u := User{UserIn{UserBoth: uboth}, UserOut{UserBoth: uboth}}
err := json.Unmarshal([]byte(in), &amp;u.UserIn)
fmt.Printf(&quot;%+v %+v %v\n&quot;, u, u.UserIn.UserBoth, err)

// Fetch / set ID from db:
u.ID = 456
out, err := json.Marshal(u.UserOut)
fmt.Println(string(out), err)

Try this one on the Go Playground.

huangapple
  • 本文由 发表于 2017年2月7日 17:25:53
  • 转载请务必保留本文链接:https://go.coder-hub.com/42086071.html
匿名

发表评论

匿名网友

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

确定