Add unixtime serializer
This commit is contained in:
parent
992042b46e
commit
c59748da97
@ -172,16 +172,20 @@ func (schema *Schema) ParseField(fieldStruct reflect.StructField) *Field {
|
||||
if v, isSerializer := fieldValue.Interface().(SerializerInterface); isSerializer {
|
||||
field.DataType = String
|
||||
field.Serializer = v
|
||||
} else if name, ok := field.TagSettings["SERIALIZER"]; ok {
|
||||
field.DataType = String
|
||||
if strings.ToLower(name) == "json" {
|
||||
field.Serializer = JSONSerializer{}
|
||||
} else {
|
||||
schema.err = fmt.Errorf("invalid serializer type %v", name)
|
||||
var serializerName = field.TagSettings["JSON"]
|
||||
if serializerName == "" {
|
||||
serializerName = field.TagSettings["SERIALIZER"]
|
||||
}
|
||||
} else if _, ok := field.TagSettings["JSON"]; ok {
|
||||
if serializerName != "" {
|
||||
if serializer, ok := GetSerializer(serializerName); ok {
|
||||
// Set default data type to string for serializer
|
||||
field.DataType = String
|
||||
field.Serializer = JSONSerializer{}
|
||||
field.Serializer = serializer
|
||||
} else {
|
||||
schema.err = fmt.Errorf("invalid serializer type %v", serializerName)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if num, ok := field.TagSettings["AUTOINCREMENTINCREMENT"]; ok {
|
||||
@ -430,9 +434,9 @@ func (field *Field) setupValuerAndSetter() {
|
||||
if field.Serializer != nil {
|
||||
field.NewValuePool = &sync.Pool{
|
||||
New: func() interface{} {
|
||||
return &Serializer{
|
||||
return &serializer{
|
||||
Field: field,
|
||||
Interface: reflect.New(reflect.Indirect(reflect.ValueOf(field.Serializer)).Type()).Interface().(SerializerInterface),
|
||||
Serializer: reflect.New(reflect.Indirect(reflect.ValueOf(field.Serializer)).Type()).Interface().(SerializerInterface),
|
||||
}
|
||||
},
|
||||
}
|
||||
@ -489,14 +493,14 @@ func (field *Field) setupValuerAndSetter() {
|
||||
return value, zero
|
||||
}
|
||||
|
||||
serializer, ok := value.(SerializerValuerInterface)
|
||||
s, ok := value.(SerializerValuerInterface)
|
||||
if !ok {
|
||||
serializer = field.Serializer
|
||||
s = field.Serializer
|
||||
}
|
||||
|
||||
return Serializer{
|
||||
return serializer{
|
||||
Field: field,
|
||||
Valuer: serializer,
|
||||
SerializeValuer: s,
|
||||
Destination: v,
|
||||
Context: ctx,
|
||||
fieldValue: value,
|
||||
@ -583,19 +587,6 @@ func (field *Field) setupValuerAndSetter() {
|
||||
}
|
||||
|
||||
// Set
|
||||
if field.Serializer != nil {
|
||||
field.Set = func(ctx context.Context, value reflect.Value, v interface{}) error {
|
||||
if serializer, ok := v.(*Serializer); ok {
|
||||
serializer.Interface.Scan(ctx, field, value, serializer.value)
|
||||
fallbackSetter(ctx, value, serializer.Interface, field.Set)
|
||||
serializer.Interface = reflect.New(reflect.Indirect(reflect.ValueOf(field.Serializer)).Type()).Interface().(SerializerInterface)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
switch field.FieldType.Kind() {
|
||||
case reflect.Bool:
|
||||
field.Set = func(ctx context.Context, value reflect.Value, v interface{}) error {
|
||||
@ -917,4 +908,33 @@ func (field *Field) setupValuerAndSetter() {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if field.Serializer != nil {
|
||||
var (
|
||||
oldFieldSetter = field.Set
|
||||
sameElemType bool
|
||||
sameType = field.FieldType == reflect.ValueOf(field.Serializer).Type()
|
||||
)
|
||||
|
||||
if reflect.ValueOf(field.Serializer).Kind() == reflect.Ptr {
|
||||
sameElemType = field.FieldType == reflect.ValueOf(field.Serializer).Type().Elem()
|
||||
}
|
||||
|
||||
field.Set = func(ctx context.Context, value reflect.Value, v interface{}) (err error) {
|
||||
if s, ok := v.(*serializer); ok {
|
||||
if err = s.Serializer.Scan(ctx, field, value, s.value); err == nil {
|
||||
if sameElemType {
|
||||
field.ReflectValueOf(ctx, value).Set(reflect.ValueOf(s.Serializer).Elem())
|
||||
s.Serializer = reflect.New(reflect.Indirect(reflect.ValueOf(field.Serializer)).Type()).Interface().(SerializerInterface)
|
||||
} else if sameType {
|
||||
field.ReflectValueOf(ctx, value).Set(reflect.ValueOf(s.Serializer))
|
||||
s.Serializer = reflect.New(reflect.Indirect(reflect.ValueOf(field.Serializer)).Type()).Interface().(SerializerInterface)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
err = oldFieldSetter(ctx, value, v)
|
||||
}
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,13 +1,6 @@
|
||||
package schema
|
||||
|
||||
import (
|
||||
"context"
|
||||
"database/sql/driver"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"reflect"
|
||||
|
||||
"gorm.io/gorm/clause"
|
||||
)
|
||||
|
||||
@ -22,71 +15,6 @@ type FieldNewValuePool interface {
|
||||
Put(interface{})
|
||||
}
|
||||
|
||||
// Serializer field value serializer
|
||||
type Serializer struct {
|
||||
Field *Field
|
||||
Interface SerializerInterface
|
||||
Valuer SerializerValuerInterface
|
||||
Destination reflect.Value
|
||||
Context context.Context
|
||||
value interface{}
|
||||
fieldValue interface{}
|
||||
}
|
||||
|
||||
// Scan implements sql.Scanner interface
|
||||
func (s *Serializer) Scan(value interface{}) error {
|
||||
s.value = value
|
||||
return nil
|
||||
}
|
||||
|
||||
// Value implements driver.Valuer interface
|
||||
func (s Serializer) Value() (driver.Value, error) {
|
||||
return s.Valuer.Value(s.Context, s.Field, s.Destination, s.fieldValue)
|
||||
}
|
||||
|
||||
// SerializerInterface serializer interface
|
||||
type SerializerInterface interface {
|
||||
Scan(ctx context.Context, field *Field, dst reflect.Value, dbValue interface{}) error
|
||||
SerializerValuerInterface
|
||||
}
|
||||
|
||||
// SerializerValuerInterface serializer valuer interface
|
||||
type SerializerValuerInterface interface {
|
||||
Value(ctx context.Context, field *Field, dst reflect.Value, fieldValue interface{}) (interface{}, error)
|
||||
}
|
||||
|
||||
// JSONSerializer json serializer
|
||||
type JSONSerializer struct {
|
||||
}
|
||||
|
||||
// Scan implements serializer interface
|
||||
func (JSONSerializer) Scan(ctx context.Context, field *Field, dst reflect.Value, dbValue interface{}) (err error) {
|
||||
fieldValue := reflect.New(field.FieldType)
|
||||
|
||||
if dbValue != nil {
|
||||
var bytes []byte
|
||||
switch v := dbValue.(type) {
|
||||
case []byte:
|
||||
bytes = v
|
||||
case string:
|
||||
bytes = []byte(v)
|
||||
default:
|
||||
return errors.New(fmt.Sprint("Failed to unmarshal JSONB value:", dbValue))
|
||||
}
|
||||
|
||||
err = json.Unmarshal(bytes, fieldValue.Interface())
|
||||
}
|
||||
|
||||
field.ReflectValueOf(ctx, dst).Set(fieldValue.Elem())
|
||||
return
|
||||
}
|
||||
|
||||
// Value implements serializer interface
|
||||
func (JSONSerializer) Value(ctx context.Context, field *Field, dst reflect.Value, fieldValue interface{}) (interface{}, error) {
|
||||
result, err := json.Marshal(fieldValue)
|
||||
return string(result), err
|
||||
}
|
||||
|
||||
// CreateClausesInterface create clauses interface
|
||||
type CreateClausesInterface interface {
|
||||
CreateClauses(*Field) []clause.Interface
|
||||
|
125
schema/serializer.go
Normal file
125
schema/serializer.go
Normal file
@ -0,0 +1,125 @@
|
||||
package schema
|
||||
|
||||
import (
|
||||
"context"
|
||||
"database/sql"
|
||||
"database/sql/driver"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"reflect"
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
)
|
||||
|
||||
var serializerMap = sync.Map{}
|
||||
|
||||
// RegisterSerializer register serializer
|
||||
func RegisterSerializer(name string, serializer SerializerInterface) {
|
||||
serializerMap.Store(strings.ToLower(name), serializer)
|
||||
}
|
||||
|
||||
// GetSerializer get serializer
|
||||
func GetSerializer(name string) (serializer SerializerInterface, ok bool) {
|
||||
v, ok := serializerMap.Load(strings.ToLower(name))
|
||||
if ok {
|
||||
serializer, ok = v.(SerializerInterface)
|
||||
}
|
||||
return serializer, ok
|
||||
}
|
||||
|
||||
func init() {
|
||||
RegisterSerializer("json", JSONSerializer{})
|
||||
RegisterSerializer("unixtime", UnixSecondSerializer{})
|
||||
}
|
||||
|
||||
// Serializer field value serializer
|
||||
type serializer struct {
|
||||
Field *Field
|
||||
Serializer SerializerInterface
|
||||
SerializeValuer SerializerValuerInterface
|
||||
Destination reflect.Value
|
||||
Context context.Context
|
||||
value interface{}
|
||||
fieldValue interface{}
|
||||
}
|
||||
|
||||
// Scan implements sql.Scanner interface
|
||||
func (s *serializer) Scan(value interface{}) error {
|
||||
s.value = value
|
||||
return nil
|
||||
}
|
||||
|
||||
// Value implements driver.Valuer interface
|
||||
func (s serializer) Value() (driver.Value, error) {
|
||||
return s.SerializeValuer.Value(s.Context, s.Field, s.Destination, s.fieldValue)
|
||||
}
|
||||
|
||||
// SerializerInterface serializer interface
|
||||
type SerializerInterface interface {
|
||||
Scan(ctx context.Context, field *Field, dst reflect.Value, dbValue interface{}) error
|
||||
SerializerValuerInterface
|
||||
}
|
||||
|
||||
// SerializerValuerInterface serializer valuer interface
|
||||
type SerializerValuerInterface interface {
|
||||
Value(ctx context.Context, field *Field, dst reflect.Value, fieldValue interface{}) (interface{}, error)
|
||||
}
|
||||
|
||||
// JSONSerializer json serializer
|
||||
type JSONSerializer struct {
|
||||
}
|
||||
|
||||
// Scan implements serializer interface
|
||||
func (JSONSerializer) Scan(ctx context.Context, field *Field, dst reflect.Value, dbValue interface{}) (err error) {
|
||||
fieldValue := reflect.New(field.FieldType)
|
||||
|
||||
if dbValue != nil {
|
||||
var bytes []byte
|
||||
switch v := dbValue.(type) {
|
||||
case []byte:
|
||||
bytes = v
|
||||
case string:
|
||||
bytes = []byte(v)
|
||||
default:
|
||||
return errors.New(fmt.Sprint("Failed to unmarshal JSONB value:", dbValue))
|
||||
}
|
||||
|
||||
err = json.Unmarshal(bytes, fieldValue.Interface())
|
||||
}
|
||||
|
||||
field.ReflectValueOf(ctx, dst).Set(fieldValue.Elem())
|
||||
return
|
||||
}
|
||||
|
||||
// Value implements serializer interface
|
||||
func (JSONSerializer) Value(ctx context.Context, field *Field, dst reflect.Value, fieldValue interface{}) (interface{}, error) {
|
||||
result, err := json.Marshal(fieldValue)
|
||||
return string(result), err
|
||||
}
|
||||
|
||||
// UnixSecondSerializer json serializer
|
||||
type UnixSecondSerializer struct {
|
||||
}
|
||||
|
||||
// Scan implements serializer interface
|
||||
func (UnixSecondSerializer) Scan(ctx context.Context, field *Field, dst reflect.Value, dbValue interface{}) (err error) {
|
||||
t := sql.NullTime{}
|
||||
if err = t.Scan(dbValue); err == nil {
|
||||
err = field.Set(ctx, dst, t.Time)
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// Value implements serializer interface
|
||||
func (UnixSecondSerializer) Value(ctx context.Context, field *Field, dst reflect.Value, fieldValue interface{}) (result interface{}, err error) {
|
||||
switch v := fieldValue.(type) {
|
||||
case int64, int, uint, uint64, int32, uint32, int16, uint16:
|
||||
result = time.Unix(reflect.ValueOf(v).Int(), 0)
|
||||
default:
|
||||
err = fmt.Errorf("invalid field type %#v for UnixSecondSerializer, only int, uint supported", v)
|
||||
}
|
||||
return
|
||||
}
|
@ -7,6 +7,7 @@ import (
|
||||
"reflect"
|
||||
"strings"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"gorm.io/gorm"
|
||||
"gorm.io/gorm/schema"
|
||||
@ -17,6 +18,8 @@ type SerializerStruct struct {
|
||||
gorm.Model
|
||||
Name []byte `gorm:"json"`
|
||||
Roles Roles `gorm:"serializer:json"`
|
||||
Contracts map[string]interface{} `gorm:"serializer:json"`
|
||||
CreatedTime int64 `gorm:"serializer:unixtime;type:time"` // store time in db, use int as field type
|
||||
EncryptedString EncryptedString
|
||||
}
|
||||
|
||||
@ -45,10 +48,14 @@ func TestSerializer(t *testing.T) {
|
||||
t.Fatalf("no error should happen when migrate scanner, valuer struct, got error %v", err)
|
||||
}
|
||||
|
||||
createdAt := time.Date(2020, 1, 1, 0, 0, 0, 0, time.UTC)
|
||||
|
||||
data := SerializerStruct{
|
||||
Name: []byte("jinzhu"),
|
||||
Roles: []string{"r1", "r2"},
|
||||
Contracts: map[string]interface{}{"name": "jinzhu", "age": 10},
|
||||
EncryptedString: EncryptedString("pass"),
|
||||
CreatedTime: createdAt.Unix(),
|
||||
}
|
||||
|
||||
if err := DB.Create(&data).Error; err != nil {
|
||||
|
Loading…
x
Reference in New Issue
Block a user