Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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

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

like image 967
KOGI Avatar asked May 23 '17 22:05

KOGI


2 Answers

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.

like image 50
Schwern Avatar answered Nov 15 '22 09:11

Schwern


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.

like image 30
klvmungai Avatar answered Nov 15 '22 10:11

klvmungai