diamond-orm/json.go

320 lines
7.7 KiB
Go

package orm
import (
"encoding/json"
"fmt"
"github.com/fatih/structtag"
"reflect"
"time"
)
func defaultEngine() *Engine {
return engines.Engines[defaultKey]
}
func anyToModel(input any) *Model {
rv := reflect.TypeOf(input)
for rv.Kind() == reflect.Ptr ||
rv.Kind() == reflect.Interface ||
rv.Kind() == reflect.Slice || rv.Kind() == reflect.Pointer {
rv = rv.Elem()
}
maybeEngine := defaultEngine()
if maybeEngine == nil {
return nil
}
return maybeEngine.modelMap.Map[rv.Name()]
}
func JSONSerialize(input any, pretty bool) ([]byte, error) {
vp := reflect.ValueOf(input)
vt := reflect.TypeOf(input)
if vt.Kind() != reflect.Pointer {
return nil, fmt.Errorf("Argument must be a pointer or pointer to a slice; got: %v", vt.Kind())
}
ser, err := innerSerialize(vp)
if err != nil {
return nil, err
}
if pretty {
return json.MarshalIndent(ser, "", "\t")
}
return json.Marshal(ser)
}
func JSONDeserialize(val any, ser []byte) error {
var fiv any
if err := json.Unmarshal(ser, &fiv); err != nil {
return err
}
vp := reflect.ValueOf(val)
if vp.Kind() != reflect.Pointer {
return fmt.Errorf("Argument must be a pointer or pointer to a slice; got: %v", vp.Kind())
}
m := anyToModel(val)
if m == nil {
return fmt.Errorf("No model found for type '%s'", vp.Type().Name())
}
maybeEngine := defaultEngine()
if maybeEngine == nil {
return fmt.Errorf("No engines have been created!?")
}
fv, err := innerDeserialize(fiv, m, maybeEngine)
if err != nil {
return err
}
vp.Elem().Set(fv)
return nil
}
func innerSerialize(v reflect.Value) (ret any, err error) {
switch v.Kind() {
case reflect.Interface:
v = v.Elem()
fallthrough
case reflect.Pointer:
if v.IsNil() {
return ret, nil
}
for v.Kind() == reflect.Ptr {
v = v.Elem()
}
if v.IsZero() {
return ret, nil
}
fallthrough
case reflect.Struct:
m := anyToModel(v.Interface())
if m == nil {
if canConvertTo[time.Time](v.Type()) {
ret = v.Interface().(time.Time).Format(time.RFC3339)
} else {
var bytes []byte
bytes, err = json.Marshal(v.Interface())
if err != nil {
return nil, err
}
ser := make(map[string]any)
err = json.Unmarshal(bytes, &ser)
if err != nil {
return nil, err
}
ret = ser
}
} else {
depopulated, depopulatedId := isDepopulated(v, m.IDField)
if depopulated {
ret = depopulatedId
} else {
rmap := make(map[string]any)
for i := range v.NumField() {
fv := v.Field(i)
ft := v.Type().Field(i)
var tag *structtag.Tags
tag, err = structtag.Parse(string(ft.Tag))
if err != nil {
return nil, err
}
var jsonTag *structtag.Tag
jsonTag, err = tag.Get("json")
if err != nil || jsonTag.Name == "-" {
continue
}
if jsonTag.Name == "" {
// we are dealing with an inlined/anonymous struct
var maybeMap any
maybeMap, err = innerSerialize(fv)
if amap, ok := maybeMap.(map[string]any); ok {
for k, vv := range amap {
rmap[k] = vv
}
}
} else {
rmap[jsonTag.Name], err = innerSerialize(fv)
if err != nil {
return nil, err
}
}
}
ret = rmap
}
}
case reflect.Slice, reflect.Array:
ret0 := make([]any, 0)
for i := range v.Len() {
var ser any
ser, err = innerSerialize(v.Index(i))
if err != nil {
return nil, err
}
ret0 = append(ret0, ser)
}
ret = ret0
default:
ret = v.Interface()
}
return ret, nil
}
func innerDeserialize(input any, m *Model, e *Engine) (nv reflect.Value, err error) {
t := m.Type
irv := reflect.ValueOf(input)
if irv.Kind() == reflect.Slice || irv.Kind() == reflect.Array {
nv = reflect.MakeSlice(reflect.SliceOf(t), 0, 0)
for i := range irv.Len() {
var snv reflect.Value
cur := irv.Index(i)
snv, err = innerDeserialize(cur.Interface(), m, e)
if err != nil {
return snv, err
}
nv = reflect.Append(nv, snv)
}
} else { // it's a map or primitive value
nv = reflect.New(t).Elem()
if asMap, ok := input.(map[string]any); ok {
for _, f := range m.Fields {
if f.Index < 0 {
continue
}
ft := f.Original
fv := nv.Field(f.Index)
var tags *structtag.Tags
var btag *structtag.Tag
tags, err = structtag.Parse(string(ft.Tag))
if err != nil {
return
}
btag, err = tags.Get("json")
if err != nil || btag.Name == "-" {
continue
}
interm := asMap[btag.Name]
var tmp any
if str, sok := interm.(string); sok {
if ttmp, terr := time.Parse(time.RFC3339, str); terr == nil {
tmp = ttmp
} else {
tmp = interm
}
} else {
tmp = interm
}
switch fv.Kind() {
case reflect.Int64, reflect.Int32, reflect.Int, reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
if tmp != nil {
fv.Set(reflect.ValueOf(tmp).Convert(ft.Type))
}
case reflect.Array, reflect.Slice:
if interm != nil {
slic := reflect.ValueOf(interm)
fv.Set(handleSliceMaybe(slic, fv.Type().Elem()))
}
default:
if ft.Anonymous {
var nfv reflect.Value
nfv, err = handleAnon(input, ft.Type)
if err != nil {
return
}
fv.Set(nfv)
} else {
fv.Set(reflect.ValueOf(tmp))
}
}
}
for _, r := range m.Relationships {
if r.Type == BelongsTo || r.Type == ManyToOne || r.Idx < 0 {
continue
}
ft := r.OriginalField
fv := nv.Field(r.Idx)
var tags *structtag.Tags
var btag *structtag.Tag
tags, err = structtag.Parse(string(ft.Tag))
if err != nil {
return
}
btag, err = tags.Get("json")
if err != nil || btag.Name == "-" {
continue
}
var rv reflect.Value
interm := asMap[btag.Name]
rv, err = innerDeserialize(interm, r.RelatedModel, e)
if err != nil {
return reflect.Value{}, err
}
fv.Set(rv)
}
} else {
iface := nv.Addr().Interface()
err = e.Model(iface).Where(fmt.Sprintf("%s = ?", m.IDField), input).Find(iface)
if err != nil {
return reflect.Value{}, err
}
}
}
return
}
func handleAnon(raw any, rtype reflect.Type) (nv reflect.Value, err error) {
nv = reflect.New(rtype).Elem()
amap, ok := raw.(map[string]any)
if ok {
for i := range rtype.NumField() {
ft := rtype.Field(i)
fv := nv.Field(i)
var tags *structtag.Tags
var btag *structtag.Tag
tags, err = structtag.Parse(string(ft.Tag))
if err != nil {
return
}
btag, terr := tags.Get("json")
if terr != nil || btag.Name == "-" || !ft.IsExported() {
continue
}
fval := amap[btag.Name]
if reflect.TypeOf(fval) == reflect.TypeFor[string]() && ft.Type == reflect.TypeFor[time.Time]() {
tt, _ := time.Parse(time.RFC3339, fval.(string))
fv.Set(reflect.ValueOf(tt))
} else if fval != nil {
fv.Set(reflect.ValueOf(fval))
}
}
}
return
}
func handleSliceMaybe(iv reflect.Value, dstType reflect.Type) reflect.Value {
if iv.Kind() != reflect.Slice && iv.Kind() != reflect.Pointer {
return iv
}
dst := reflect.MakeSlice(reflect.SliceOf(dstType), 0, 0)
for i := range iv.Len() {
//dst.Set(reflect.Append(fv, handleSliceMaybe(iv.Index(i).Elem())))
maybeIface := iv.Index(i)
if maybeIface.Kind() == reflect.Interface {
maybeIface = maybeIface.Elem()
}
maybeNdest := dstType
if maybeNdest.Kind() == reflect.Slice || maybeNdest.Kind() == reflect.Array {
maybeNdest = maybeNdest.Elem()
}
dst = reflect.Append(dst, handleSliceMaybe(maybeIface, maybeNdest))
}
return dst
}
func isDepopulated(v reflect.Value, idField string) (bool, any) {
for v.Kind() == reflect.Ptr {
v = v.Elem()
}
syn := reflect.New(v.Type()).Elem()
syn.FieldByName(idField).Set(v.FieldByName(idField))
finalId := v.FieldByName(idField).Interface()
return reflect.DeepEqual(v.Interface(), syn.Interface()), finalId
}