#Implementing sql.Null* custom JSON marshaller/unmarshaller makes it unusable with libraries

6 messages · Page 1 of 1 (latest)

sterile lance
#

You could implement sql valuer/scanner in addition to marshaler/unmarshaler.

#

It would, if you embed them.

#

You could do smth like this (not tested):

type NullString struct {
    sql.NullString
}

func (s *NullString) MarshalJSON() ([]byte, error) {
    if !s.Valid {
        return json.Marshal(nil)
    }
    return json.Marshal(s.String)
}

func (s *NullString) UnmarshalJSON(b []byte) error {
    if bytes.Equal(b, []byte("null")) {
        s.String = ""
        s.Valid = false
        return nil
    }
    return json.Unmarshal(b, &s.String)
}

func main() {
    var s any = &NullString{}
    _ = s.(sql.Scanner)
    _ = s.(driver.Valuer)
    _ = s.(json.Marshaler)
    _ = s.(json.Unmarshaler)
}
sterile lance
sterile lance
#

You're absolutely right, I believe it's better to separate those. And if the service is complex enough your API objects are looking nothing like DB objects or logical entities, so you're probably already doing some sort of conversion between those. That being said, I've definitely used the own null structs approach for CRUD-ish services where the same type is used throughout db, api and logic, to keep things simple.