From 508cb7db88b4b366c5b8e0910fbf3e883ad4c2db Mon Sep 17 00:00:00 2001 From: John Date: Mon, 15 Jun 2020 16:46:48 +0800 Subject: [PATCH] add and improve Scan/ScanDeep feature for package gdb/gvar/gjson/gconv --- container/gvar/gvar_struct.go | 20 +++- database/gdb/gdb_model_select.go | 4 +- encoding/gjson/gjson_api.go | 32 +++++- encoding/gjson/gjson_z_unit_struct_test.go | 110 +++++++++++++++++++++ util/gconv/gconv_scan.go | 48 +++++++++ util/gconv/gconv_z_unit_scan_test.go | 77 +++++++++++++++ 6 files changed, 283 insertions(+), 8 deletions(-) create mode 100644 util/gconv/gconv_scan.go create mode 100644 util/gconv/gconv_z_unit_scan_test.go diff --git a/container/gvar/gvar_struct.go b/container/gvar/gvar_struct.go index c963be690..b24da76c5 100644 --- a/container/gvar/gvar_struct.go +++ b/container/gvar/gvar_struct.go @@ -6,7 +6,9 @@ package gvar -import "github.com/gogf/gf/util/gconv" +import ( + "github.com/gogf/gf/util/gconv" +) // Struct maps value of to . // The parameter should be a pointer to a struct instance. @@ -31,3 +33,19 @@ func (v *Var) Structs(pointer interface{}, mapping ...map[string]string) (err er func (v *Var) StructsDeep(pointer interface{}, mapping ...map[string]string) (err error) { return gconv.StructsDeep(v.Val(), pointer, mapping...) } + +// Scan automatically calls Struct or Structs function according to the type of parameter +// to implement the converting. +// It calls function Struct if is type of *struct/**struct to do the converting. +// It calls function Structs if is type of *[]struct/*[]*struct to do the converting. +func (v *Var) Scan(pointer interface{}, mapping ...map[string]string) (err error) { + return gconv.Scan(v.Val(), pointer, mapping...) +} + +// ScanDeep automatically calls StructDeep or StructsDeep function according to the type of +// parameter to implement the converting. +// It calls function StructDeep if is type of *struct/**struct to do the converting. +// It calls function StructsDeep if is type of *[]struct/*[]*struct to do the converting. +func (v *Var) ScanDeep(pointer interface{}, mapping ...map[string]string) (err error) { + return gconv.ScanDeep(v.Val(), pointer, mapping...) +} diff --git a/database/gdb/gdb_model_select.go b/database/gdb/gdb_model_select.go index 3ba19a73a..153ed3da3 100644 --- a/database/gdb/gdb_model_select.go +++ b/database/gdb/gdb_model_select.go @@ -226,13 +226,11 @@ func (m *Model) Scan(pointer interface{}, where ...interface{}) error { return fmt.Errorf("params should be type of pointer, but got: %v", k) } switch t.Elem().Kind() { - case reflect.Array: - case reflect.Slice: + case reflect.Array, reflect.Slice: return m.Structs(pointer, where...) default: return m.Struct(pointer, where...) } - return nil } // Count does "SELECT COUNT(x) FROM ..." statement for the model. diff --git a/encoding/gjson/gjson_api.go b/encoding/gjson/gjson_api.go index 6df82414d..149e19264 100644 --- a/encoding/gjson/gjson_api.go +++ b/encoding/gjson/gjson_api.go @@ -329,27 +329,39 @@ func (j *Json) GetStructsDeep(pattern string, pointer interface{}, mapping ...ma return gconv.StructsDeep(j.Get(pattern), pointer, mapping...) } -// GetMapToMap retrieves the value by specified and converts it specified map variable. +// GetScan automatically calls Struct or Structs function according to the type of parameter +// to implement the converting.. +func (j *Json) GetScan(pattern string, pointer interface{}, mapping ...map[string]string) error { + return gconv.Scan(j.Get(pattern), pointer, mapping...) +} + +// GetScanDeep automatically calls StructDeep or StructsDeep function according to the type of +// parameter to implement the converting.. +func (j *Json) GetScanDeep(pattern string, pointer interface{}, mapping ...map[string]string) error { + return gconv.ScanDeep(j.Get(pattern), pointer, mapping...) +} + +// GetMapToMap retrieves the value by specified and converts it to specified map variable. // See gconv.MapToMap. func (j *Json) GetMapToMap(pattern string, pointer interface{}, mapping ...map[string]string) error { return gconv.MapToMap(j.Get(pattern), pointer, mapping...) } -// GetMapToMapDeep retrieves the value by specified and converts it specified map +// GetMapToMapDeep retrieves the value by specified and converts it to specified map // variable recursively. // See gconv.MapToMapDeep. func (j *Json) GetMapToMapDeep(pattern string, pointer interface{}, mapping ...map[string]string) error { return gconv.MapToMapDeep(j.Get(pattern), pointer, mapping...) } -// GetMapToMaps retrieves the value by specified and converts it specified map slice +// GetMapToMaps retrieves the value by specified and converts it to specified map slice // variable. // See gconv.MapToMaps. func (j *Json) GetMapToMaps(pattern string, pointer interface{}, mapping ...map[string]string) error { return gconv.MapToMaps(j.Get(pattern), pointer, mapping...) } -// GetMapToMapsDeep retrieves the value by specified and converts it specified map slice +// GetMapToMapsDeep retrieves the value by specified and converts it to specified map slice // variable recursively. // See gconv.MapToMapsDeep. func (j *Json) GetMapToMapsDeep(pattern string, pointer interface{}, mapping ...map[string]string) error { @@ -404,6 +416,18 @@ func (j *Json) ToStructsDeep(pointer interface{}, mapping ...map[string]string) return gconv.StructsDeep(*(j.p), pointer, mapping...) } +// ToScan automatically calls Struct or Structs function according to the type of parameter +// to implement the converting.. +func (j *Json) ToScan(pointer interface{}, mapping ...map[string]string) error { + return gconv.Scan(*(j.p), pointer, mapping...) +} + +// ToScanDeep automatically calls StructDeep or StructsDeep function according to the type of +// parameter to implement the converting.. +func (j *Json) ToScanDeep(pointer interface{}, mapping ...map[string]string) error { + return gconv.ScanDeep(*(j.p), pointer, mapping...) +} + // ToMapToMap converts current Json object to specified map variable. // The parameter of should be type of *map. func (j *Json) ToMapToMap(pointer interface{}, mapping ...map[string]string) error { diff --git a/encoding/gjson/gjson_z_unit_struct_test.go b/encoding/gjson/gjson_z_unit_struct_test.go index 6f8a95cae..e55bea233 100644 --- a/encoding/gjson/gjson_z_unit_struct_test.go +++ b/encoding/gjson/gjson_z_unit_struct_test.go @@ -12,6 +12,116 @@ import ( "testing" ) +func Test_GetScan(t *testing.T) { + type User struct { + Name string + Score float64 + } + j := gjson.New(`[{"name":"john", "score":"100"},{"name":"smith", "score":"60"}]`) + gtest.C(t, func(t *gtest.T) { + var user *User + err := j.GetScan("1", &user) + t.Assert(err, nil) + t.Assert(user, &User{ + Name: "smith", + Score: 60, + }) + }) + gtest.C(t, func(t *gtest.T) { + var users []User + err := j.GetScan(".", &users) + t.Assert(err, nil) + t.Assert(users, []User{ + { + Name: "john", + Score: 100, + }, + { + Name: "smith", + Score: 60, + }, + }) + }) +} + +func Test_GetScanDeep(t *testing.T) { + type User struct { + Name string + Score float64 + } + j := gjson.New(`[{"name":"john", "score":"100"},{"name":"smith", "score":"60"}]`) + gtest.C(t, func(t *gtest.T) { + var user *User + err := j.GetScanDeep("1", &user) + t.Assert(err, nil) + t.Assert(user, &User{ + Name: "smith", + Score: 60, + }) + }) + gtest.C(t, func(t *gtest.T) { + var users []User + err := j.GetScanDeep(".", &users) + t.Assert(err, nil) + t.Assert(users, []User{ + { + Name: "john", + Score: 100, + }, + { + Name: "smith", + Score: 60, + }, + }) + }) +} + +func Test_ToScan(t *testing.T) { + type User struct { + Name string + Score float64 + } + j := gjson.New(`[{"name":"john", "score":"100"},{"name":"smith", "score":"60"}]`) + gtest.C(t, func(t *gtest.T) { + var users []User + err := j.ToScan(&users) + t.Assert(err, nil) + t.Assert(users, []User{ + { + Name: "john", + Score: 100, + }, + { + Name: "smith", + Score: 60, + }, + }) + }) +} + +func Test_ToScanDeep(t *testing.T) { + type User struct { + Name string + Score float64 + } + j := gjson.New(`[{"name":"john", "score":"100"},{"name":"smith", "score":"60"}]`) + gtest.C(t, func(t *gtest.T) { + var users []User + err := j.ToScanDeep(&users) + t.Assert(err, nil) + t.Assert(users, []User{ + { + Name: "john", + Score: 100, + }, + { + Name: "smith", + Score: 60, + }, + }) + }) +} + func Test_ToStruct1(t *testing.T) { gtest.C(t, func(t *gtest.T) { type BaseInfoItem struct { diff --git a/util/gconv/gconv_scan.go b/util/gconv/gconv_scan.go new file mode 100644 index 000000000..43194c67e --- /dev/null +++ b/util/gconv/gconv_scan.go @@ -0,0 +1,48 @@ +// Copyright 2017 gf Author(https://github.com/gogf/gf). All Rights Reserved. +// +// This Source Code Form is subject to the terms of the MIT License. +// If a copy of the MIT was not distributed with this file, +// You can obtain one at https://github.com/gogf/gf. + +package gconv + +import ( + "fmt" + "reflect" +) + +// Scan automatically calls Struct or Structs function according to the type of parameter +// to implement the converting. +// It calls function Struct if is type of *struct/**struct to do the converting. +// It calls function Structs if is type of *[]struct/*[]*struct to do the converting. +func Scan(params interface{}, pointer interface{}, mapping ...map[string]string) (err error) { + t := reflect.TypeOf(pointer) + k := t.Kind() + if k != reflect.Ptr { + return fmt.Errorf("params should be type of pointer, but got: %v", k) + } + switch t.Elem().Kind() { + case reflect.Array, reflect.Slice: + return Structs(params, pointer, mapping...) + default: + return Struct(params, pointer, mapping...) + } +} + +// ScanDeep automatically calls StructDeep or StructsDeep function according to the type of +// parameter to implement the converting.. +// It calls function StructDeep if is type of *struct/**struct to do the converting. +// It calls function StructsDeep if is type of *[]struct/*[]*struct to do the converting. +func ScanDeep(params interface{}, pointer interface{}, mapping ...map[string]string) (err error) { + t := reflect.TypeOf(pointer) + k := t.Kind() + if k != reflect.Ptr { + return fmt.Errorf("params should be type of pointer, but got: %v", k) + } + switch t.Elem().Kind() { + case reflect.Array, reflect.Slice: + return StructsDeep(params, pointer, mapping...) + default: + return StructDeep(params, pointer, mapping...) + } +} diff --git a/util/gconv/gconv_z_unit_scan_test.go b/util/gconv/gconv_z_unit_scan_test.go new file mode 100644 index 000000000..768a44560 --- /dev/null +++ b/util/gconv/gconv_z_unit_scan_test.go @@ -0,0 +1,77 @@ +// Copyright 2020 gf Author(https://github.com/gogf/gf). All Rights Reserved. +// +// This Source Code Form is subject to the terms of the MIT License. +// If a copy of the MIT was not distributed with this file, +// You can obtain one at https://github.com/gogf/gf. + +package gconv_test + +import ( + "github.com/gogf/gf/frame/g" + "github.com/gogf/gf/test/gtest" + "github.com/gogf/gf/util/gconv" + "testing" +) + +func Test_Scan(t *testing.T) { + type User struct { + Uid int + Name string + Pass1 string `gconv:"password1"` + Pass2 string `gconv:"password2"` + } + gtest.C(t, func(t *gtest.T) { + var ( + user = new(User) + params = g.Map{ + "uid": 1, + "name": "john", + "PASS1": "123", + "PASS2": "456", + } + ) + err := gconv.Scan(params, user) + t.Assert(err, nil) + t.Assert(user, &User{ + Uid: 1, + Name: "john", + Pass1: "123", + Pass2: "456", + }) + }) + gtest.C(t, func(t *gtest.T) { + var ( + users []User + params = g.Slice{ + g.Map{ + "uid": 1, + "name": "john1", + "PASS1": "111", + "PASS2": "222", + }, + g.Map{ + "uid": 2, + "name": "john2", + "PASS1": "333", + "PASS2": "444", + }, + } + ) + err := gconv.Scan(params, &users) + t.Assert(err, nil) + t.Assert(users, g.Slice{ + &User{ + Uid: 1, + Name: "john1", + Pass1: "111", + Pass2: "222", + }, + &User{ + Uid: 2, + Name: "john2", + Pass1: "333", + Pass2: "444", + }, + }) + }) +}