Custom unmarshalling from flat json to nested struct

Advertisements

Lets say I have two struct that are related like this:

type SecretUser struct {
    UserInfo `json:"userInfo"`
    Password string `json:"password"`
}

type UserInfo struct {
    FirstName string `json:"firstName"`
    LastName  string `json:"lastName"`
    Email     string `json:"email"`
}

And I receive a JSON in this form:

{
    "firstName": "nice",
    "lastName":"guy",
    "email":"nice@guy.co.uk",
    "password":"abc123"
}

I want to unmarshall this JSON into a SecretUser. Is there a better way than doing it like this?

func (u *User) UnmarshalJSON(data []byte) error {
    var objmap map[string]*json.RawMessage
    var password string
    var err error

    err = json.Unmarshal(data, &objmap)
    if err != nil {
        return err
    }

    if err := json.Unmarshal(data, &u.UserInfo); err != nil {
        return err
    }
    
    err = json.Unmarshal(*objmap["password"], &password)
    if err != nil {
        return err
    }

    u.Password = password
    return nil
}

Basically, I partially unmarshall the JSON into a UserInfo struct and then read it again to extract the password. I don’t want to create another struct for just unmarshalling this JSON cleanly or use an external library (unless it’s part of the standard). Is there a more clean/efficient way of doing this, without reading the JSON twice or setting every field manually from a map?

>Solution :

Simply include the UserData into the SecretUser struct and do not specify a json tag for it.

type UserInfo struct {
    FirstName string `json:"firstName"`
    LastName  string `json:"lastName"`
    Email     string `json:"email"`
}

type SecretUser struct {
    UserInfo
    Password string `json:"password"`
}

func main() {
    data := []byte(`{"firstName": "nice","lastName":"guy","email":"nice@guy.co.uk","password":"abc123"}`)
    var u SecretUser
    _ := json.Unmarshal(data, &u)
    fmt.Println(u)
}

Go Play Space example

Leave a ReplyCancel reply