英文:
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-type
为application/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 ID
s), 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
,以避免重复结构值)在UserIn
和UserOut
中都被嵌入,因为我们希望这些字段都出现在两者中。
现在,如果你有一个包含所有字段的输入(即使我们不想要所有字段):
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:"name"`
Age int `json:"age"`
}
type UserIn struct {
*UserBoth
Email string `json:"email"`
}
type UserOut struct {
*UserBoth
ID uint `json:"id"`
}
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 = `{"name":"Bob","age":23,"Email":"as@as.com","id":123}`
Unmarshaling into a value of UserIn
will only parse what you want:
uin := UserIn{UserBoth: &UserBoth{}}
err := json.Unmarshal([]byte(in), &uin)
fmt.Printf("%+v %+v %v\n", uin, uin.UserBoth, err)
Output (note that the Email
field is parsed but ID
isn't):
{UserBoth:0x1050e150 Email:as@as.com} &{Name:Bob Age:23} <nil>
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):
{"name":"Bob","age":23,"id":456} <nil>
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 := &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)
// 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.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论