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 {
|
if v, isSerializer := fieldValue.Interface().(SerializerInterface); isSerializer {
|
||||||
field.DataType = String
|
field.DataType = String
|
||||||
field.Serializer = v
|
field.Serializer = v
|
||||||
} else if name, ok := field.TagSettings["SERIALIZER"]; ok {
|
} else {
|
||||||
field.DataType = String
|
var serializerName = field.TagSettings["JSON"]
|
||||||
if strings.ToLower(name) == "json" {
|
if serializerName == "" {
|
||||||
field.Serializer = JSONSerializer{}
|
serializerName = field.TagSettings["SERIALIZER"]
|
||||||
} else {
|
}
|
||||||
schema.err = fmt.Errorf("invalid serializer type %v", name)
|
if serializerName != "" {
|
||||||
|
if serializer, ok := GetSerializer(serializerName); ok {
|
||||||
|
// Set default data type to string for serializer
|
||||||
|
field.DataType = String
|
||||||
|
field.Serializer = serializer
|
||||||
|
} else {
|
||||||
|
schema.err = fmt.Errorf("invalid serializer type %v", serializerName)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
} else if _, ok := field.TagSettings["JSON"]; ok {
|
|
||||||
field.DataType = String
|
|
||||||
field.Serializer = JSONSerializer{}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if num, ok := field.TagSettings["AUTOINCREMENTINCREMENT"]; ok {
|
if num, ok := field.TagSettings["AUTOINCREMENTINCREMENT"]; ok {
|
||||||
@ -430,9 +434,9 @@ func (field *Field) setupValuerAndSetter() {
|
|||||||
if field.Serializer != nil {
|
if field.Serializer != nil {
|
||||||
field.NewValuePool = &sync.Pool{
|
field.NewValuePool = &sync.Pool{
|
||||||
New: func() interface{} {
|
New: func() interface{} {
|
||||||
return &Serializer{
|
return &serializer{
|
||||||
Field: field,
|
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,17 +493,17 @@ func (field *Field) setupValuerAndSetter() {
|
|||||||
return value, zero
|
return value, zero
|
||||||
}
|
}
|
||||||
|
|
||||||
serializer, ok := value.(SerializerValuerInterface)
|
s, ok := value.(SerializerValuerInterface)
|
||||||
if !ok {
|
if !ok {
|
||||||
serializer = field.Serializer
|
s = field.Serializer
|
||||||
}
|
}
|
||||||
|
|
||||||
return Serializer{
|
return serializer{
|
||||||
Field: field,
|
Field: field,
|
||||||
Valuer: serializer,
|
SerializeValuer: s,
|
||||||
Destination: v,
|
Destination: v,
|
||||||
Context: ctx,
|
Context: ctx,
|
||||||
fieldValue: value,
|
fieldValue: value,
|
||||||
}, false
|
}, false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -583,19 +587,6 @@ func (field *Field) setupValuerAndSetter() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Set
|
// 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() {
|
switch field.FieldType.Kind() {
|
||||||
case reflect.Bool:
|
case reflect.Bool:
|
||||||
field.Set = func(ctx context.Context, value reflect.Value, v interface{}) error {
|
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
|
package schema
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
|
||||||
"database/sql/driver"
|
|
||||||
"encoding/json"
|
|
||||||
"errors"
|
|
||||||
"fmt"
|
|
||||||
"reflect"
|
|
||||||
|
|
||||||
"gorm.io/gorm/clause"
|
"gorm.io/gorm/clause"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -22,71 +15,6 @@ type FieldNewValuePool interface {
|
|||||||
Put(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
|
// CreateClausesInterface create clauses interface
|
||||||
type CreateClausesInterface interface {
|
type CreateClausesInterface interface {
|
||||||
CreateClauses(*Field) []clause.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"
|
"reflect"
|
||||||
"strings"
|
"strings"
|
||||||
"testing"
|
"testing"
|
||||||
|
"time"
|
||||||
|
|
||||||
"gorm.io/gorm"
|
"gorm.io/gorm"
|
||||||
"gorm.io/gorm/schema"
|
"gorm.io/gorm/schema"
|
||||||
@ -15,8 +16,10 @@ import (
|
|||||||
|
|
||||||
type SerializerStruct struct {
|
type SerializerStruct struct {
|
||||||
gorm.Model
|
gorm.Model
|
||||||
Name []byte `gorm:"json"`
|
Name []byte `gorm:"json"`
|
||||||
Roles Roles `gorm:"serializer: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
|
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)
|
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{
|
data := SerializerStruct{
|
||||||
Name: []byte("jinzhu"),
|
Name: []byte("jinzhu"),
|
||||||
Roles: []string{"r1", "r2"},
|
Roles: []string{"r1", "r2"},
|
||||||
|
Contracts: map[string]interface{}{"name": "jinzhu", "age": 10},
|
||||||
EncryptedString: EncryptedString("pass"),
|
EncryptedString: EncryptedString("pass"),
|
||||||
|
CreatedTime: createdAt.Unix(),
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := DB.Create(&data).Error; err != nil {
|
if err := DB.Create(&data).Error; err != nil {
|
||||||
|
Loading…
x
Reference in New Issue
Block a user