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 }