如何在输入时跳过Struct中的JSON字段,在输出时显示它们,在输入时接受某些字段,在Golang中跳过它们?

时间:2022-07-05 21:29:20

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

我正在编写一个使用Echo framework和Gorm的webservice。我有一个这样的用户结构:

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}

我接受内容类型为application/json的POST请求。我希望我的输入是{"email_address": "email@email.com"},输出是{"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?

我怎么能禁止用户在请求中提交ID(这样他们就不能用某些ID创建记录),而在响应中保留ID呢?现在我在做用户。在保存之前ID = 0,但我想知道有没有更好的方法?

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!

谢谢!

2 个解决方案

#1


1  

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:

这可以在不重复使用嵌入字段的情况下优雅地解决。您可以创建三个单独的类型;一个用于公共字段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"`
}

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.

注意UserBoth(或者更确切地说是*UserBoth,以避免重复struct值)都嵌入到UserIn和UserOut中,因为我们希望这两个字段都包含。

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:

解组到UserIn值将只解析您想要的:

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

输出(注意邮件字段被解析,但ID不是):

{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):

输出(注意ID字段存在,但电子邮件不存在):

{"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.

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

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.

试试这个在Go游乐场。

#2


0  

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

虽然icza的回答提出了一个不错的解决方案,但您也可以使用JSON编组辅助方法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.

去操场上。

#1


1  

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:

这可以在不重复使用嵌入字段的情况下优雅地解决。您可以创建三个单独的类型;一个用于公共字段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"`
}

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.

注意UserBoth(或者更确切地说是*UserBoth,以避免重复struct值)都嵌入到UserIn和UserOut中,因为我们希望这两个字段都包含。

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:

解组到UserIn值将只解析您想要的:

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

输出(注意邮件字段被解析,但ID不是):

{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):

输出(注意ID字段存在,但电子邮件不存在):

{"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.

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

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.

试试这个在Go游乐场。

#2


0  

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

虽然icza的回答提出了一个不错的解决方案,但您也可以使用JSON编组辅助方法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.

去操场上。