From 3faaa4d15205f8a90893bea6ea896fc45322f074 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vin=C3=ADcius=20Garcia?= Date: Sun, 28 Feb 2021 13:07:28 -0300 Subject: [PATCH] Add feature for automatically serialize fields tagged as "*,json" This commit implements the features, but it still needs tests to confirm it is working. --- json.go | 30 ++++++++++++++++++++++++++++++ kiss_orm.go | 22 ++++++++++++++++++---- structs/structs.go | 20 +++++++++++++++----- 3 files changed, 63 insertions(+), 9 deletions(-) create mode 100644 json.go diff --git a/json.go b/json.go new file mode 100644 index 0000000..a78d0a5 --- /dev/null +++ b/json.go @@ -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) +} diff --git a/kiss_orm.go b/kiss_orm.go index 5ca4e8f..3808988 100644 --- a/kiss_orm.go +++ b/kiss_orm.go @@ -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...) diff --git a/structs/structs.go b/structs/structs.go index e298539..0c5be01 100644 --- a/structs/structs.go +++ b/structs/structs.go @@ -3,6 +3,7 @@ package structs import ( "fmt" "reflect" + "strings" "github.com/pkg/errors" ) @@ -13,9 +14,10 @@ type structInfo struct { } type fieldInfo struct { - Name string - Index int - Valid bool + 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, + Name: name, + Index: i, + SerializeAsJSON: serializeAsJSON, }) }