How do I omit a struct field *only* when marshalling, but keep it when unmarshalling?

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

How do I omit a struct field *only* when marshalling, but keep it when unmarshalling?

问题

我有一个包含密码字段的User结构体。当我通过POST的JSON创建用户(或使用新密码更新用户)时,我希望将密码字段接受/解组到我的对象中,但是每当我返回一个用户时,我希望省略密码字段。以下是我目前能想到的最好的解决方案。它可以工作,但需要在我想要消除的字段名称周围进行大量重复(如果我添加一个像FirstName这样的新字段,我必须在3个不同的位置添加它)。

在仍然遵守结构体上的json标签的情况下,我该如何更好地实现这一点?

func main() {
    origJson := []byte(`{"id":"1","username":"Chad","pwd":"sillypants"}`)
    fmt.Println("Original:     " + string(origJson))
    
    var unmarshalled User
    json.Unmarshal(origJson, &unmarshalled)
    fmt.Printf("Unmarshalled: %+v\n", unmarshalled)

    marshalled, _ := json.Marshal(unmarshalled)
    fmt.Println("ReMarshalled: " + string(marshalled))
}

type User struct {
    Id       string `json:"id"`
    Username string `json:"username"`
    Password string `json:"pwd"`
}

type SafeUser struct {
    Id       string `json:"id"`
    Username string `json:"username"`
}

func (u User) MarshalJSON() ([]byte, error) {
    safeUser := SafeUser{
        Id      : u.Id,
        Username: u.Username,
    }

    return json.Marshal(safeUser)
}

在Go Playground上尝试一下

英文:

I have a User struct with a password field. When I'm creating a user (or udpating with a new password) via POSTed JSON, I want to accept/unmarshal the password field into my object, but whenever I return a user, I want to omit the password field. Below is the best I've been able to come up with so far. It works, but it requires a lot of duplication around the field names that I'd like to eliminate (right now, if I add a new field like FirstName, I have to add that in 3 separate places).

How do I do this better while still honoring the json tags on the struct?

func main() {
	origJson := []byte(`{"id":"1","username":"Chad","pwd":"sillypants"}`)
	fmt.Println("Original:     " + string(origJson))
	
	var unmarshalled User
	json.Unmarshal(origJson, &unmarshalled)
	fmt.Printf("Unmarshalled: %+v\n", unmarshalled)

	marshalled, _ := json.Marshal(unmarshalled)
	fmt.Println("ReMarshalled: " + string(marshalled))
}

type User struct {
	Id       string `json:"id"`
	Username string `json:"username"`
	Password string `json:"pwd"`
}

type SafeUser struct {
	Id       string `json:"id"`
	Username string `json:"username"`
}

func (u User) MarshalJSON() ([]byte, error) {
	safeUser := SafeUser{
		Id      : u.Id,
		Username: u.Username,
	}

	return json.Marshal(safeUser)
}

Try it on the Go Playground

答案1

得分: 6

利用嵌入结构体的特性。定义一个User结构体,并将其嵌入到UnsafeUser结构体中,UnsafeUser结构体添加了password字段(以及其他任何字段,比如支付信息)。

type User struct {
    Id       string `json:"id"`
    Username string `json:"username"`
}

type UnsafeUser struct {
    User
    Password string `json:"pwd"`
}

(最好默认情况下保持安全,并声明哪些是不安全的,就像Go的unsafe包一样。)

然后,你可以在不需要知道和复制所有字段的情况下从UnsafeUser中提取User。

func (uu UnsafeUser) MarshalJSON() ([]byte, error) {
    return json.Marshal(uu.User)
}
$ go run ~/tmp/test.go
Original:     {"id":"1","username":"Chad","pwd":"sillypants"}
Unmarshalled: {User:{Id:1 Username:Chad} Password:sillypants}
ReMarshalled: {"id":"1","username":"Chad"}

注意,你可以看到在解组UnsafeUser时嵌入的User结构体。

英文:

Take advantage of embedded structs. Define a User, and embed that in an UnsafeUser which adds the password field (and anything else, like payment info).

type User struct {
    Id       string `json:"id"`
    Username string `json:"username"`
}

type UnsafeUser struct {
	User
    Password string `json:"pwd"`
}

(It's better to make things safe by default and declare what is unsafe, like Go's unsafe pacakge.)

Then you can extract the User from within the UnsafeUser without having to know and copy all the fields.

func (uu UnsafeUser) MarshalJSON() ([]byte, error) {
    return json.Marshal(uu.User)
}

$ go run ~/tmp/test.go
Original:     {"id":"1","username":"Chad","pwd":"sillypants"}
Unmarshalled: {User:{Id:1 Username:Chad} Password:sillypants}
ReMarshalled: {"id":"1","username":"Chad"}

Note how you can see the User struct embedded within the unmarshalled UnsafeUser.

答案2

得分: 5

我遇到了同样的问题,但是我找到了这篇文章

这个想法是使用嵌入和匿名结构体来覆盖字段。

func (u User) MarshalJSON() ([]byte, error) {
    type Alias User
    safeUser := struct {
        Password string `json:"pwd,omitempty"`
        Alias
    }{
        // Leave out the password so that it is empty
        Alias: Alias(u),
    }

    return json.Marshal(safeUser)
}

试一试

Alias 帮助防止在编组时出现无限循环。

请注意,为了使覆盖生效,你必须保持相同的 JSON 字段名称。

英文:

I had the same issue but came across this article.

The idea is to use embedding and an anonymous struct to override fields.

func (u User) MarshalJSON() ([]byte, error) {
    type Alias User
    safeUser := struct {
	    Password string `json:"pwd,omitempty"`
	    Alias
    }{
        // Leave out the password so that it is empty
	    Alias: Alias(u),
    }

    return json.Marshal(safeUser)
}

Try it

The Alias helps prevent an infinite loop while marshalling.

Please note that you have to maintain the same JSON field name for the override to work.

huangapple
  • 本文由 发表于 2017年5月24日 06:33:42
  • 转载请务必保留本文链接:https://go.coder-hub.com/44146114.html
匿名

发表评论

匿名网友

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

确定