Add feature for automatically serialize fields tagged as "*,json"

This commit implements the features, but it still needs tests
to confirm it is working.
pull/2/head
Vinícius Garcia 2021-02-28 13:07:28 -03:00
parent f5b7ab8028
commit 3faaa4d152
3 changed files with 63 additions and 9 deletions

30
json.go Normal file
View File

@ -0,0 +1,30 @@
package kissorm
import (
"database/sql/driver"
"encoding/json"
"fmt"
)
// This type was created to make it easier to adapt
// input attributes to be convertible to and from JSON
// before sending or receiving it from the database.
type jsonSerializable struct {
Attr interface{}
}
// Scan Implements the Scanner interface in order to load
// this field from the JSON stored in the database
func (j *jsonSerializable) Scan(value interface{}) error {
rawJSON, ok := value.([]byte)
if !ok {
return fmt.Errorf("unexpected type received to Scan: %T", value)
}
return json.Unmarshal(rawJSON, j.Attr)
}
// Value Implements the Valuer interface in order to save
// this field as JSON on the database.
func (j jsonSerializable) Value() (driver.Value, error) {
return json.Marshal(j.Attr)
}

View File

@ -554,6 +554,12 @@ func buildInsertQuery(
return "", nil, err
}
t := reflect.TypeOf(record)
if t.Kind() == reflect.Ptr {
t = t.Elem()
}
info := structs.GetTagInfo(t)
for _, fieldName := range idFieldNames {
// Remove any ID field that was not set:
if reflect.ValueOf(recordMap[fieldName]).IsZero() {
@ -569,7 +575,12 @@ func buildInsertQuery(
params = make([]interface{}, len(recordMap))
valuesQuery := make([]string, len(recordMap))
for i, col := range columnNames {
params[i] = recordMap[col]
recordValue := recordMap[col]
params[i] = recordValue
if info.ByName(col).SerializeAsJSON {
params[i] = jsonSerializable{Attr: recordValue}
}
valuesQuery[i] = dialect.Placeholder(i)
}
@ -724,7 +735,7 @@ func parseInputFunc(fn interface{}) (reflect.Type, error) {
type nopScanner struct{}
var nopScannerValue = reflect.ValueOf(&nopScanner{})
var nopScannerValue = reflect.ValueOf(&nopScanner{}).Interface()
func (nopScanner) Scan(value interface{}) error {
return nil
@ -757,10 +768,13 @@ func scanRows(rows *sql.Rows, record interface{}) error {
valueScanner := nopScannerValue
if fieldInfo.Valid {
valueScanner = v.Field(fieldInfo.Index).Addr()
valueScanner = v.Field(fieldInfo.Index).Addr().Interface()
if fieldInfo.SerializeAsJSON {
valueScanner = jsonSerializable{Attr: valueScanner}
}
}
scanArgs = append(scanArgs, valueScanner.Interface())
scanArgs = append(scanArgs, valueScanner)
}
return rows.Scan(scanArgs...)

View File

@ -3,6 +3,7 @@ package structs
import (
"fmt"
"reflect"
"strings"
"github.com/pkg/errors"
)
@ -16,6 +17,7 @@ type fieldInfo struct {
Name string
Index int
Valid bool
SerializeAsJSON bool
}
func (s structInfo) ByIndex(idx int) *fieldInfo {
@ -296,9 +298,17 @@ func getTagNames(t reflect.Type) structInfo {
continue
}
tags := strings.Split(name, ",")
serializeAsJSON := false
if len(tags) > 1 {
name = tags[0]
serializeAsJSON = tags[1] == "json"
}
info.Add(fieldInfo{
Name: name,
Index: i,
SerializeAsJSON: serializeAsJSON,
})
}