Serializer 是一个可扩展的接口,允许自定义如何使用数据库对数据进行序列化和反序列化

GORM 提供了一些默认的序列化器:json、gob、unixtime,这里有一个如何使用它的快速示例

  1. type User struct {
  2. Name []byte `gorm:"serializer:json"`
  3. Roles Roles `gorm:"serializer:json"`
  4. Contracts map[string]interface{} `gorm:"serializer:json"`
  5. JobInfo Job `gorm:"type:bytes;serializer:gob"`
  6. CreatedTime int64 `gorm:"serializer:unixtime;type:time"` // 将 int 作为日期时间存储到数据库中
  7. }
  8. type Roles []string
  9. type Job struct {
  10. Title string
  11. Location string
  12. IsIntern bool
  13. }

注册序列化器

一个Serializer需要实现如何对数据进行序列化和反序列化,所以需要实现如下接口

  1. import "gorm.io/gorm/schema"
  2. type SerializerInterface interface {
  3. Scan(ctx context.Context, field *schema.Field, dst reflect.Value, dbValue interface{}) error
  4. SerializerValuerInterface
  5. }
  6. type SerializerValuerInterface interface {
  7. Value(ctx context.Context, field *schema.Field, dst reflect.Value, fieldValue interface{}) (interface{}, error)
  8. }

例如,默认 JSONSerializer 的实现如下:

  1. // JSONSerializer json序列化器
  2. type JSONSerializer struct {
  3. }
  4. // 实现 Scan 方法
  5. func (JSONSerializer) Scan(ctx context.Context, field *Field, dst reflect.Value, dbValue interface{}) (err error) {
  6. fieldValue := reflect.New(field.FieldType)
  7. if dbValue != nil {
  8. var bytes []byte
  9. switch v := dbValue.(type) {
  10. case []byte:
  11. bytes = v
  12. case string:
  13. bytes = []byte(v)
  14. default:
  15. return fmt.Errorf("failed to unmarshal JSONB value: %#v", dbValue)
  16. }
  17. err = json.Unmarshal(bytes, fieldValue.Interface())
  18. }
  19. field.ReflectValueOf(ctx, dst).Set(fieldValue.Elem())
  20. return
  21. }
  22. // 实现 Value 方法
  23. func (JSONSerializer) Value(ctx context.Context, field *Field, dst reflect.Value, fieldValue interface{}) (interface{}, error) {
  24. return json.Marshal(fieldValue)
  25. }

并使用以下代码注册:

  1. schema.RegisterSerializer("json", JSONSerializer{})

注册序列化器后,您可以将其与 serializer 标签一起使用,例如:

  1. type User struct {
  2. Name []byte `gorm:"serializer:json"`
  3. }

自定义序列化器类型

你可以通过标签使用已注册的序列化器,你也可以自定义 struct,实现上述的 SerializerInterface 接口,随后便可以直接将其作为字段类型使用,例如:

  1. type EncryptedString string
  2. // ctx: contains request-scoped values
  3. // field: the field using the serializer, contains GORM settings, struct tags
  4. // dst: current model value, `user` in the below example
  5. // dbValue: current field's value in database
  6. func (es *EncryptedString) Scan(ctx context.Context, field *schema.Field, dst reflect.Value, dbValue interface{}) (err error) {
  7. switch value := dbValue.(type) {
  8. case []byte:
  9. *es = EncryptedString(bytes.TrimPrefix(value, []byte("hello")))
  10. case string:
  11. *es = EncryptedString(strings.TrimPrefix(value, "hello"))
  12. default:
  13. return fmt.Errorf("unsupported data %#v", dbValue)
  14. }
  15. return nil
  16. }
  17. // ctx: contains request-scoped values
  18. // field: the field using the serializer, contains GORM settings, struct tags
  19. // dst: current model value, `user` in the below example
  20. // fieldValue: current field's value of the dst
  21. func (es EncryptedString) Value(ctx context.Context, field *schema.Field, dst reflect.Value, fieldValue interface{}) (interface{}, error) {
  22. return "hello" + string(es), nil
  23. }
  24. type User struct {
  25. gorm.Model
  26. Password EncryptedString
  27. }
  28. data := User{
  29. Password: EncryptedString("pass"),
  30. }
  31. DB.Create(&data)
  32. // INSERT INTO `serializer_structs` (`password`) VALUES ("hellopass")
  33. var result User
  34. DB.First(&result, "id = ?", data.ID)
  35. // result => User{
  36. // Password: EncryptedString("pass"),
  37. // }
  38. DB.Where(User{Password: EncryptedString("pass")}).Take(&result)
  39. // SELECT * FROM `users` WHERE `users`.`password` = "hellopass"