Home > Mobile >  Convert sub struct to string and back
Convert sub struct to string and back

Time:10-01

I am having some trouble with the UnmarshalJSON and MarshalJSON i am not sure i understand them correctly or atleast i have an error that i have not been able to spot and in my 3 hours of googling i haven't been able find, so here goes.

I have the following User struct

type User struct {
    ID        uuid.UUID            `gorm:"primaryKey,type:string,size:36,<-:create" json:"id"`
    Username  string               `gorm:"unique" json:"username"`
    Password  PasswordHash         `gorm:"type:string" json:"password"`
    CreatedAt time.Time            `gorm:"autoCreateTime:milli" json:"created_at"`
    UpdatedAt time.Time            `gorm:"autoUpdateTime:milli" json:"updated_at,omitempty"`
    DeletedAt gorm.DeletedAt       `gorm:"index" json:"deleted_at,omitempty"`
}

which when i try to convert into JSON, with the json.Marshal function from the build in encoding/json library, i get the following:

{"id":"3843298e-74d4-4dd7-8eff-007ab34a4c19","username":"root","password":{},"created_at":"0001-01-01T00:00:00Z","updated_at":"0001-01-01T00:00:00Z","deleted_at":null}

I expected some thing like:

{"id":"3843298e-74d4-4dd7-8eff-007ab34a4c19","username":"root","password":"$argon2id$v=19$m=4194304,t=1,p=64$Z9EFSTk26TQxx Qv9g58gQ$4At0rvvv9trRcFZmSMXY0nISBuEt 1X8mCRAYbyXqSs","created_at":"0001-01-01T00:00:00Z","updated_at":"0001-01-01T00:00:00Z","deleted_at":null}

i can't seem to get the password field to be a string of the hashed password. even when i have the following methods on the PasswordHash struct

type Params struct {
    memory      uint32
    iterations  uint32
    parallelism uint8
    saltLength  uint32
    keyLength   uint32
}

type PasswordHash struct {
    hash   []byte
    salt   []byte
    params Params
}

func (h *PasswordHash) String() string {
    b64Salt := base64.RawStdEncoding.EncodeToString(h.salt)
    b64Hash := base64.RawStdEncoding.EncodeToString(h.hash)
    return fmt.Sprintf(
        "$%s$v=%d$m=%d,t=%d,p=%d$%s$%s",
        algoName,
        argon2.Version,
        h.params.memory,
        h.params.iterations,
        h.params.parallelism,
        b64Salt,
        b64Hash,
    )
}

func (h *PasswordHash) UnmarshalJSON(data []byte) error {
    var v string
    if err := json.Unmarshal(data, &v); err != nil {
        return err
    }
    params, salt, hash, err := decodeHash(v)
    if err != nil {
        return err
    }
    h.params = params
    h.salt = salt
    h.hash = hash
    return nil
}

func (h *PasswordHash) MarshalJSON() ([]byte, error) {
    //return []byte(h.String()), nil
    return json.Marshal(h.String())
}

So i guess my question is, shouldn't the MarshalJSON be called on the PasswordHash struct when trying to convert the user to JSON? and if so how come i can't seem to get it to be a string value?

CodePudding user response:

You have MarshalJSON defined with a receiver of *PasswordHash, but your value is type PasswordHash. Change the receiver to PasswordHash and it works as expected: https://go.dev/play/p/WukE_5JBEPL

  • Related