fix issue @1380

This commit is contained in:
John Guo 2021-09-15 21:17:45 +08:00
parent 64a5cedcdf
commit 68e760d13a
17 changed files with 542 additions and 176 deletions

View File

@ -14,11 +14,6 @@ import (
"github.com/gogf/gf/util/gconv"
)
// Interface converts and returns `r` as type of interface{}.
func (r Record) Interface() interface{} {
return r
}
// Json converts `r` to JSON format content.
func (r Record) Json() string {
content, _ := gparser.VarToJson(r.Map())

View File

@ -13,11 +13,6 @@ import (
"math"
)
// Interface converts and returns `r` as type of interface{}.
func (r Result) Interface() interface{} {
return r
}
// IsEmpty checks and returns whether `r` is empty.
func (r Result) IsEmpty() bool {
return r.Len() == 0
@ -33,7 +28,7 @@ func (r Result) Size() int {
return r.Len()
}
// Chunk splits an Result into multiple Results,
// Chunk splits a Result into multiple Results,
// the size of each array is determined by `size`.
// The last chunk may contain less than size elements.
func (r Result) Chunk(size int) []Result {

View File

@ -14,6 +14,7 @@ import (
"github.com/gogf/gf/frame/g"
"github.com/gogf/gf/os/gtime"
"github.com/gogf/gf/test/gtest"
"github.com/gogf/gf/text/gstr"
"github.com/gogf/gf/util/gconv"
"reflect"
"testing"
@ -480,3 +481,119 @@ func Test_Scan_AutoFilteringByStructAttributes(t *testing.T) {
t.Assert(users[0].Id, 1)
})
}
func Test_Scan_JsonAttributes(t *testing.T) {
type GiftImage struct {
Uid string `json:"uid"`
Url string `json:"url"`
Status string `json:"status"`
Name string `json:"name"`
}
type GiftComment struct {
Name string `json:"name"`
Field string `json:"field"`
Required bool `json:"required"`
}
type Prop struct {
Name string `json:"name"`
Values []string `json:"values"`
}
type Sku struct {
GiftId int64 `json:"gift_id"`
Name string `json:"name"`
ScorePrice int `json:"score_price"`
MarketPrice int `json:"market_price"`
CostPrice int `json:"cost_price"`
Stock int `json:"stock"`
}
type Covers struct {
List []GiftImage `json:"list"`
}
type GiftEntity struct {
Id int64 `json:"id"`
StoreId int64 `json:"store_id"`
GiftType int `json:"gift_type"`
GiftName string `json:"gift_name"`
Description string `json:"description"`
Covers Covers `json:"covers"`
Cover string `json:"cover"`
GiftCategoryId []int64 `json:"gift_category_id"`
HasProps bool `json:"has_props"`
OutSn string `json:"out_sn"`
IsLimitSell bool `json:"is_limit_sell"`
LimitSellType int `json:"limit_sell_type"`
LimitSellCycle string `json:"limit_sell_cycle"`
LimitSellCycleCount int `json:"limit_sell_cycle_count"`
LimitSellCustom bool `json:"limit_sell_custom"` // 只允许特定会员兑换
LimitCustomerTags []int64 `json:"limit_customer_tags"` // 允许兑换的成员
ScorePrice int `json:"score_price"`
MarketPrice float64 `json:"market_price"`
CostPrice int `json:"cost_price"`
Stock int `json:"stock"`
Props []Prop `json:"props"`
Skus []Sku `json:"skus"`
ExpressType []string `json:"express_type"`
Comments []GiftComment `json:"comments"`
Content string `json:"content"`
AtLeastRechargeCount int `json:"at_least_recharge_count"`
Status int `json:"status"`
}
type User struct {
Id int
Passport string
}
var (
table = "jfy_gift"
)
array := gstr.SplitAndTrim(gtest.TestDataContent(`issue1380.sql`), ";")
for _, v := range array {
if _, err := db.Exec(v); err != nil {
gtest.Error(err)
}
}
defer dropTable(table)
gtest.C(t, func(t *gtest.T) {
var (
entity = new(GiftEntity)
err = db.Model(table).Where("id", 17).Scan(entity)
)
t.AssertNil(err)
t.Assert(len(entity.Skus), 2)
t.Assert(entity.Skus[0].Name, "red")
t.Assert(entity.Skus[0].Stock, 10)
t.Assert(entity.Skus[0].GiftId, 1)
t.Assert(entity.Skus[0].CostPrice, 80)
t.Assert(entity.Skus[0].ScorePrice, 188)
t.Assert(entity.Skus[0].MarketPrice, 388)
t.Assert(entity.Skus[1].Name, "blue")
t.Assert(entity.Skus[1].Stock, 100)
t.Assert(entity.Skus[1].GiftId, 2)
t.Assert(entity.Skus[1].CostPrice, 81)
t.Assert(entity.Skus[1].ScorePrice, 200)
t.Assert(entity.Skus[1].MarketPrice, 288)
t.Assert(entity.Id, 17)
t.Assert(entity.StoreId, 100004)
t.Assert(entity.GiftType, 1)
t.Assert(entity.GiftName, "GIFT")
t.Assert(entity.Description, "支持个性定制的父亲节老师长辈的专属礼物")
t.Assert(len(entity.Covers.List), 3)
t.Assert(entity.OutSn, "259402")
t.Assert(entity.LimitCustomerTags, "[]")
t.Assert(entity.ScorePrice, 10)
t.Assert(len(entity.Props), 1)
t.Assert(len(entity.Comments), 2)
t.Assert(entity.Status, 99)
t.Assert(entity.Content, `<p>礼品详情</p>`)
})
}

35
database/gdb/testdata/issue1380.sql vendored Normal file
View File

@ -0,0 +1,35 @@
CREATE TABLE `jfy_gift` (
`id` int(0) UNSIGNED NOT NULL AUTO_INCREMENT,
`gift_name` varchar(32) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL COMMENT '礼品名称',
`at_least_recharge_count` int(0) UNSIGNED NOT NULL DEFAULT 1 COMMENT '最少兑换数量',
`comments` json NOT NULL COMMENT '礼品留言',
`content` text CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL COMMENT '礼品详情',
`cost_price` decimal(10, 2) NULL DEFAULT NULL COMMENT '成本价',
`cover` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL COMMENT '封面',
`covers` json NOT NULL COMMENT '礼品图片库',
`description` varchar(62) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL DEFAULT '' COMMENT '礼品备注',
`express_type` json NOT NULL COMMENT '配送方式',
`gift_type` int(0) NOT NULL COMMENT '礼品类型1实物2虚拟3优惠券4积分券',
`has_props` tinyint(1) NOT NULL DEFAULT 0 COMMENT '是否有多个属性',
`is_limit_sell` tinyint(0) UNSIGNED NOT NULL DEFAULT 0 COMMENT '是否限购',
`limit_customer_tags` json NOT NULL COMMENT '语序购买的会员标签',
`limit_sell_custom` tinyint(0) UNSIGNED NOT NULL DEFAULT 0 COMMENT '是否开启允许购买的会员标签',
`limit_sell_cycle` varchar(32) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL DEFAULT '' COMMENT '限购周期',
`limit_sell_cycle_count` int(0) NOT NULL COMMENT '限购期内允许购买的数量',
`limit_sell_type` tinyint(0) NOT NULL COMMENT '限购类型',
`market_price` decimal(10, 2) NOT NULL COMMENT '市场价',
`out_sn` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL COMMENT '内部编码',
`props` json NOT NULL COMMENT '规格',
`skus` json NOT NULL COMMENT 'SKU',
`score_price` decimal(10, 2) NOT NULL COMMENT '兑换所需积分',
`stock` int(0) NOT NULL COMMENT '库存',
`create_at` datetime(0) NOT NULL COMMENT '创建日期',
`store_id` int(0) NOT NULL COMMENT '所属商城',
`status` int(0) UNSIGNED NULL DEFAULT 1 COMMENT '1下架20审核中30复审中99上架',
`view_count` int(0) NOT NULL DEFAULT 0 COMMENT '访问量',
`sell_count` int(0) NULL DEFAULT 0 COMMENT '销量',
PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci ROW_FORMAT = Dynamic;
INSERT INTO `jfy_gift` VALUES (17, 'GIFT', 1, '[{\"name\": \"身份证\", \"field\": \"idcard\", \"required\": false}, {\"name\": \"留言2\", \"field\": \"text\", \"required\": false}]', '<p>礼品详情</p>', 0.00, '', '{\"list\": [{\"uid\": \"vc-upload-1629292486099-3\", \"url\": \"https://cdn.taobao.com/sULsYiwaOPjsKGoBXwKtuewPzACpBDfQ.jpg\", \"name\": \"O1CN01OH6PIP1Oc5ot06U17_!!922361725.jpg\", \"status\": \"done\"}, {\"uid\": \"vc-upload-1629292486099-4\", \"url\": \"https://cdn.taobao.com/lqLHDcrFTgNvlWyXfLYZwmsrODzIBtFH.jpg\", \"name\": \"O1CN018hBckI1Oc5ouc8ppl_!!922361725.jpg\", \"status\": \"done\"}, {\"uid\": \"vc-upload-1629292486099-5\", \"url\": \"https://cdn.taobao.com/pvqyutXckICmHhbPBQtrVLHuMlXuGxUg.jpg\", \"name\": \"O1CN0185Ubp91Oc5osQTTcc_!!922361725.jpg\", \"status\": \"done\"}]}', '支持个性定制的父亲节老师长辈的专属礼物', '[\"快递包邮\", \"同城配送\"]', 1, 0, 0, '[]', 0, 'day', 0, 1, 0.00, '259402', '[{\"name\": \"颜色\", \"values\": [\"红色\", \"蓝色\"]}]', '[{\"name\": \"red\", \"stock\": 10, \"gift_id\": 1, \"cost_price\": 80, \"score_price\": 188, \"market_price\": 388}, {\"name\": \"blue\", \"stock\": 100, \"gift_id\": 2, \"cost_price\": 81, \"score_price\": 200, \"market_price\": 288}]', 10.00, 0, '2021-08-18 21:26:13', 100004, 99, 0, 0);

View File

@ -219,11 +219,11 @@ func parsePatternItemValue(value string, itemType int) (int, error) {
// it converts the value to number according to predefined map.
switch itemType {
case patternItemTypeWeek:
if number, ok := monthMap[strings.ToLower(value)]; ok {
if number, ok := weekMap[strings.ToLower(value)]; ok {
return number, nil
}
case patternItemTypeMonth:
if number, ok := weekMap[strings.ToLower(value)]; ok {
if number, ok := monthMap[strings.ToLower(value)]; ok {
return number, nil
}
}

View File

@ -9,7 +9,9 @@ package gtest
import (
"fmt"
"github.com/gogf/gf/internal/empty"
"io/ioutil"
"os"
"path/filepath"
"reflect"
"testing"
@ -22,7 +24,7 @@ const (
pathFilterKey = "/test/gtest/gtest"
)
// C creates an unit testing case.
// C creates a unit testing case.
// The parameter `t` is the pointer to testing.T of stdlib (*testing.T).
// The parameter `f` is the closure function for unit testing case.
func C(t *testing.T, f func(t *T)) {
@ -35,7 +37,7 @@ func C(t *testing.T, f func(t *T)) {
f(&T{t})
}
// Case creates an unit testing case.
// Case creates a unit testing case.
// The parameter `t` is the pointer to testing.T of stdlib (*testing.T).
// The parameter `f` is the closure function for unit testing case.
// Deprecated.
@ -357,3 +359,28 @@ func AssertNil(value interface{}) {
}
AssertNE(value, nil)
}
// TestDataPath retrieves and returns the testdata path of current package,
// which is used for unit testing cases only.
// The optional parameter `names` specifies the sub-folders/sub-files,
// which will be joined with current system separator and returned with the path.
func TestDataPath(names ...string) string {
_, path, _ := gdebug.CallerWithFilter(pathFilterKey)
path = filepath.Dir(path) + string(filepath.Separator) + "testdata"
for _, name := range names {
path += string(filepath.Separator) + name
}
return path
}
// TestDataContent retrieves and returns the file content for specified testdata path of current package
func TestDataContent(names ...string) string {
path := TestDataPath(names...)
if path != "" {
data, err := ioutil.ReadFile(path)
if err == nil {
return string(data)
}
}
return ""
}

View File

@ -53,235 +53,235 @@ type doConvertInput struct {
Extra []interface{} // Extra values for implementing the converting.
}
// doConvert does commonly used types converting.
func doConvert(input doConvertInput) interface{} {
switch input.ToTypeName {
// doConvert does commonly use types converting.
func doConvert(in doConvertInput) interface{} {
switch in.ToTypeName {
case "int":
return Int(input.FromValue)
return Int(in.FromValue)
case "*int":
if _, ok := input.FromValue.(*int); ok {
return input.FromValue
if _, ok := in.FromValue.(*int); ok {
return in.FromValue
}
v := Int(input.FromValue)
v := Int(in.FromValue)
return &v
case "int8":
return Int8(input.FromValue)
return Int8(in.FromValue)
case "*int8":
if _, ok := input.FromValue.(*int8); ok {
return input.FromValue
if _, ok := in.FromValue.(*int8); ok {
return in.FromValue
}
v := Int8(input.FromValue)
v := Int8(in.FromValue)
return &v
case "int16":
return Int16(input.FromValue)
return Int16(in.FromValue)
case "*int16":
if _, ok := input.FromValue.(*int16); ok {
return input.FromValue
if _, ok := in.FromValue.(*int16); ok {
return in.FromValue
}
v := Int16(input.FromValue)
v := Int16(in.FromValue)
return &v
case "int32":
return Int32(input.FromValue)
return Int32(in.FromValue)
case "*int32":
if _, ok := input.FromValue.(*int32); ok {
return input.FromValue
if _, ok := in.FromValue.(*int32); ok {
return in.FromValue
}
v := Int32(input.FromValue)
v := Int32(in.FromValue)
return &v
case "int64":
return Int64(input.FromValue)
return Int64(in.FromValue)
case "*int64":
if _, ok := input.FromValue.(*int64); ok {
return input.FromValue
if _, ok := in.FromValue.(*int64); ok {
return in.FromValue
}
v := Int64(input.FromValue)
v := Int64(in.FromValue)
return &v
case "uint":
return Uint(input.FromValue)
return Uint(in.FromValue)
case "*uint":
if _, ok := input.FromValue.(*uint); ok {
return input.FromValue
if _, ok := in.FromValue.(*uint); ok {
return in.FromValue
}
v := Uint(input.FromValue)
v := Uint(in.FromValue)
return &v
case "uint8":
return Uint8(input.FromValue)
return Uint8(in.FromValue)
case "*uint8":
if _, ok := input.FromValue.(*uint8); ok {
return input.FromValue
if _, ok := in.FromValue.(*uint8); ok {
return in.FromValue
}
v := Uint8(input.FromValue)
v := Uint8(in.FromValue)
return &v
case "uint16":
return Uint16(input.FromValue)
return Uint16(in.FromValue)
case "*uint16":
if _, ok := input.FromValue.(*uint16); ok {
return input.FromValue
if _, ok := in.FromValue.(*uint16); ok {
return in.FromValue
}
v := Uint16(input.FromValue)
v := Uint16(in.FromValue)
return &v
case "uint32":
return Uint32(input.FromValue)
return Uint32(in.FromValue)
case "*uint32":
if _, ok := input.FromValue.(*uint32); ok {
return input.FromValue
if _, ok := in.FromValue.(*uint32); ok {
return in.FromValue
}
v := Uint32(input.FromValue)
v := Uint32(in.FromValue)
return &v
case "uint64":
return Uint64(input.FromValue)
return Uint64(in.FromValue)
case "*uint64":
if _, ok := input.FromValue.(*uint64); ok {
return input.FromValue
if _, ok := in.FromValue.(*uint64); ok {
return in.FromValue
}
v := Uint64(input.FromValue)
v := Uint64(in.FromValue)
return &v
case "float32":
return Float32(input.FromValue)
return Float32(in.FromValue)
case "*float32":
if _, ok := input.FromValue.(*float32); ok {
return input.FromValue
if _, ok := in.FromValue.(*float32); ok {
return in.FromValue
}
v := Float32(input.FromValue)
v := Float32(in.FromValue)
return &v
case "float64":
return Float64(input.FromValue)
return Float64(in.FromValue)
case "*float64":
if _, ok := input.FromValue.(*float64); ok {
return input.FromValue
if _, ok := in.FromValue.(*float64); ok {
return in.FromValue
}
v := Float64(input.FromValue)
v := Float64(in.FromValue)
return &v
case "bool":
return Bool(input.FromValue)
return Bool(in.FromValue)
case "*bool":
if _, ok := input.FromValue.(*bool); ok {
return input.FromValue
if _, ok := in.FromValue.(*bool); ok {
return in.FromValue
}
v := Bool(input.FromValue)
v := Bool(in.FromValue)
return &v
case "string":
return String(input.FromValue)
return String(in.FromValue)
case "*string":
if _, ok := input.FromValue.(*string); ok {
return input.FromValue
if _, ok := in.FromValue.(*string); ok {
return in.FromValue
}
v := String(input.FromValue)
v := String(in.FromValue)
return &v
case "[]byte":
return Bytes(input.FromValue)
return Bytes(in.FromValue)
case "[]int":
return Ints(input.FromValue)
return Ints(in.FromValue)
case "[]int32":
return Int32s(input.FromValue)
return Int32s(in.FromValue)
case "[]int64":
return Int64s(input.FromValue)
return Int64s(in.FromValue)
case "[]uint":
return Uints(input.FromValue)
return Uints(in.FromValue)
case "[]uint8":
return Bytes(input.FromValue)
return Bytes(in.FromValue)
case "[]uint32":
return Uint32s(input.FromValue)
return Uint32s(in.FromValue)
case "[]uint64":
return Uint64s(input.FromValue)
return Uint64s(in.FromValue)
case "[]float32":
return Float32s(input.FromValue)
return Float32s(in.FromValue)
case "[]float64":
return Float64s(input.FromValue)
return Float64s(in.FromValue)
case "[]string":
return Strings(input.FromValue)
return Strings(in.FromValue)
case "Time", "time.Time":
if len(input.Extra) > 0 {
return Time(input.FromValue, String(input.Extra[0]))
if len(in.Extra) > 0 {
return Time(in.FromValue, String(in.Extra[0]))
}
return Time(input.FromValue)
return Time(in.FromValue)
case "*time.Time":
var v interface{}
if len(input.Extra) > 0 {
v = Time(input.FromValue, String(input.Extra[0]))
if len(in.Extra) > 0 {
v = Time(in.FromValue, String(in.Extra[0]))
} else {
if _, ok := input.FromValue.(*time.Time); ok {
return input.FromValue
if _, ok := in.FromValue.(*time.Time); ok {
return in.FromValue
}
v = Time(input.FromValue)
v = Time(in.FromValue)
}
return &v
case "GTime", "gtime.Time":
if len(input.Extra) > 0 {
if v := GTime(input.FromValue, String(input.Extra[0])); v != nil {
if len(in.Extra) > 0 {
if v := GTime(in.FromValue, String(in.Extra[0])); v != nil {
return *v
} else {
return *gtime.New()
}
}
if v := GTime(input.FromValue); v != nil {
if v := GTime(in.FromValue); v != nil {
return *v
} else {
return *gtime.New()
}
case "*gtime.Time":
if len(input.Extra) > 0 {
if v := GTime(input.FromValue, String(input.Extra[0])); v != nil {
if len(in.Extra) > 0 {
if v := GTime(in.FromValue, String(in.Extra[0])); v != nil {
return v
} else {
return gtime.New()
}
}
if v := GTime(input.FromValue); v != nil {
if v := GTime(in.FromValue); v != nil {
return v
} else {
return gtime.New()
}
case "Duration", "time.Duration":
return Duration(input.FromValue)
return Duration(in.FromValue)
case "*time.Duration":
if _, ok := input.FromValue.(*time.Duration); ok {
return input.FromValue
if _, ok := in.FromValue.(*time.Duration); ok {
return in.FromValue
}
v := Duration(input.FromValue)
v := Duration(in.FromValue)
return &v
case "map[string]string":
return MapStrStr(input.FromValue)
return MapStrStr(in.FromValue)
case "map[string]interface{}":
return Map(input.FromValue)
return Map(in.FromValue)
case "[]map[string]interface{}":
return Maps(input.FromValue)
return Maps(in.FromValue)
default:
if input.ReferValue != nil {
if in.ReferValue != nil {
var (
referReflectValue reflect.Value
)
if v, ok := input.ReferValue.(reflect.Value); ok {
if v, ok := in.ReferValue.(reflect.Value); ok {
referReflectValue = v
} else {
referReflectValue = reflect.ValueOf(input.ReferValue)
referReflectValue = reflect.ValueOf(in.ReferValue)
}
input.ToTypeName = referReflectValue.Kind().String()
input.ReferValue = nil
return reflect.ValueOf(doConvert(input)).Convert(referReflectValue.Type()).Interface()
in.ToTypeName = referReflectValue.Kind().String()
in.ReferValue = nil
return reflect.ValueOf(doConvert(in)).Convert(referReflectValue.Type()).Interface()
}
return input.FromValue
return in.FromValue
}
}
@ -786,3 +786,22 @@ func Float64(any interface{}) float64 {
return v
}
}
// checkJsonAndUnmarshalUseNumber checks if given `any` is JSON formatted string value and does converting using `json.UnmarshalUseNumber`.
func checkJsonAndUnmarshalUseNumber(any interface{}, target interface{}) bool {
switch r := any.(type) {
case []byte:
if json.Valid(r) {
_ = json.UnmarshalUseNumber(r, &target)
return true
}
case string:
anyAsBytes := []byte(r)
if json.Valid(anyAsBytes) {
_ = json.UnmarshalUseNumber(anyAsBytes, &target)
return true
}
}
return false
}

View File

@ -48,6 +48,11 @@ type apiBytes interface {
Bytes() []byte
}
// apiInterface is used for type assert api for Interface().
type apiInterface interface {
Interface() interface{}
}
// apiInterfaces is used for type assert api for Interfaces().
type apiInterfaces interface {
Interfaces() []interface{}

View File

@ -99,7 +99,7 @@ func doMapToMap(params interface{}, pointer interface{}, mapping ...map[string]s
if e, ok := exception.(errorStack); ok {
err = e
} else {
err = gerror.NewCodeSkipf(gcode.CodeInternalError, 1, "%v", exception)
err = gerror.NewCodeSkipf(gcode.CodeInternalError, 1, "%+v", exception)
}
}
}()

View File

@ -119,7 +119,7 @@ func doMapToMaps(params interface{}, pointer interface{}, mapping ...map[string]
if e, ok := exception.(errorStack); ok {
err = e
} else {
err = gerror.NewCodeSkipf(gcode.CodeInternalError, 1, "%v", exception)
err = gerror.NewCodeSkipf(gcode.CodeInternalError, 1, "%+v", exception)
}
}
}()

View File

@ -118,6 +118,11 @@ func Float32s(any interface{}) []float32 {
if v, ok := any.(apiInterfaces); ok {
return Float32s(v.Interfaces())
}
// JSON format string value converting.
var result []float32
if checkJsonAndUnmarshalUseNumber(any, &result) {
return result
}
// Not a common type, it then uses reflection for conversion.
var reflectValue reflect.Value
if v, ok := value.(reflect.Value); ok {
@ -142,6 +147,9 @@ func Float32s(any interface{}) []float32 {
return slice
default:
if reflectValue.IsZero() {
return []float32{}
}
return []float32{Float32(any)}
}
}
@ -238,6 +246,11 @@ func Float64s(any interface{}) []float64 {
if v, ok := any.(apiInterfaces); ok {
return Floats(v.Interfaces())
}
// JSON format string value converting.
var result []float64
if checkJsonAndUnmarshalUseNumber(any, &result) {
return result
}
// Not a common type, it then uses reflection for conversion.
var reflectValue reflect.Value
if v, ok := value.(reflect.Value); ok {
@ -262,6 +275,9 @@ func Float64s(any interface{}) []float64 {
return slice
default:
if reflectValue.IsZero() {
return []float64{}
}
return []float64{Float64(any)}
}
}

View File

@ -6,7 +6,9 @@
package gconv
import "reflect"
import (
"reflect"
)
// SliceInt is alias of Ints.
func SliceInt(any interface{}) []int {
@ -18,7 +20,7 @@ func SliceInt32(any interface{}) []int32 {
return Int32s(any)
}
// SliceInt is alias of Int64s.
// SliceInt64 is alias of Int64s.
func SliceInt64(any interface{}) []int64 {
return Int64s(any)
}
@ -30,11 +32,6 @@ func Ints(any interface{}) []int {
}
var array []int
switch value := any.(type) {
case string:
if value == "" {
return []int{}
}
return []int{Int(value)}
case []string:
array = make([]int, len(value))
for k, v := range value {
@ -123,6 +120,11 @@ func Ints(any interface{}) []int {
if v, ok := any.(apiInterfaces); ok {
return Ints(v.Interfaces())
}
// JSON format string value converting.
var result []int
if checkJsonAndUnmarshalUseNumber(any, &result) {
return result
}
// Not a common type, it then uses reflection for conversion.
var reflectValue reflect.Value
if v, ok := value.(reflect.Value); ok {
@ -147,6 +149,9 @@ func Ints(any interface{}) []int {
return slice
default:
if reflectValue.IsZero() {
return []int{}
}
return []int{Int(any)}
}
}
@ -160,11 +165,6 @@ func Int32s(any interface{}) []int32 {
}
var array []int32
switch value := any.(type) {
case string:
if value == "" {
return []int32{}
}
return []int32{Int32(value)}
case []string:
array = make([]int32, len(value))
for k, v := range value {
@ -253,6 +253,11 @@ func Int32s(any interface{}) []int32 {
if v, ok := any.(apiInterfaces); ok {
return Int32s(v.Interfaces())
}
// JSON format string value converting.
var result []int32
if checkJsonAndUnmarshalUseNumber(any, &result) {
return result
}
// Not a common type, it then uses reflection for conversion.
var reflectValue reflect.Value
if v, ok := value.(reflect.Value); ok {
@ -277,6 +282,9 @@ func Int32s(any interface{}) []int32 {
return slice
default:
if reflectValue.IsZero() {
return []int32{}
}
return []int32{Int32(any)}
}
}
@ -290,11 +298,6 @@ func Int64s(any interface{}) []int64 {
}
var array []int64
switch value := any.(type) {
case string:
if value == "" {
return []int64{}
}
return []int64{Int64(value)}
case []string:
array = make([]int64, len(value))
for k, v := range value {
@ -383,6 +386,11 @@ func Int64s(any interface{}) []int64 {
if v, ok := any.(apiInterfaces); ok {
return Int64s(v.Interfaces())
}
// JSON format string value converting.
var result []int64
if checkJsonAndUnmarshalUseNumber(any, &result) {
return result
}
// Not a common type, it then uses reflection for conversion.
var reflectValue reflect.Value
if v, ok := value.(reflect.Value); ok {
@ -407,6 +415,9 @@ func Int64s(any interface{}) []int64 {
return slice
default:
if reflectValue.IsZero() {
return []int64{}
}
return []int64{Int64(any)}
}
}

View File

@ -6,7 +6,9 @@
package gconv
import "reflect"
import (
"reflect"
)
// SliceStr is alias of Strings.
func SliceStr(any interface{}) []string {
@ -104,6 +106,11 @@ func Strings(any interface{}) []string {
if v, ok := any.(apiInterfaces); ok {
return Strings(v.Interfaces())
}
// JSON format string value converting.
var result []string
if checkJsonAndUnmarshalUseNumber(any, &result) {
return result
}
// Not a common type, it then uses reflection for conversion.
var reflectValue reflect.Value
if v, ok := value.(reflect.Value); ok {
@ -128,6 +135,9 @@ func Strings(any interface{}) []string {
return slice
default:
if reflectValue.IsZero() {
return []string{}
}
return []string{String(any)}
}
}

View File

@ -119,6 +119,11 @@ func Uints(any interface{}) []uint {
if v, ok := any.(apiInterfaces); ok {
return Uints(v.Interfaces())
}
// JSON format string value converting.
var result []uint
if checkJsonAndUnmarshalUseNumber(any, &result) {
return result
}
// Not a common type, it then uses reflection for conversion.
var reflectValue reflect.Value
if v, ok := value.(reflect.Value); ok {
@ -143,6 +148,9 @@ func Uints(any interface{}) []uint {
return slice
default:
if reflectValue.IsZero() {
return []uint{}
}
return []uint{Uint(any)}
}
}
@ -244,6 +252,11 @@ func Uint32s(any interface{}) []uint32 {
if v, ok := any.(apiInterfaces); ok {
return Uint32s(v.Interfaces())
}
// JSON format string value converting.
var result []uint32
if checkJsonAndUnmarshalUseNumber(any, &result) {
return result
}
// Not a common type, it then uses reflection for conversion.
var reflectValue reflect.Value
if v, ok := value.(reflect.Value); ok {
@ -268,6 +281,9 @@ func Uint32s(any interface{}) []uint32 {
return slice
default:
if reflectValue.IsZero() {
return []uint32{}
}
return []uint32{Uint32(any)}
}
}
@ -369,6 +385,11 @@ func Uint64s(any interface{}) []uint64 {
if v, ok := any.(apiInterfaces); ok {
return Uint64s(v.Interfaces())
}
// JSON format string value converting.
var result []uint64
if checkJsonAndUnmarshalUseNumber(any, &result) {
return result
}
// Not a common type, it then uses reflection for conversion.
var reflectValue reflect.Value
if v, ok := value.(reflect.Value); ok {
@ -393,6 +414,9 @@ func Uint64s(any interface{}) []uint64 {
return slice
default:
if reflectValue.IsZero() {
return []uint64{}
}
return []uint64{Uint64(any)}
}
}

View File

@ -52,6 +52,42 @@ func StructDeep(params interface{}, pointer interface{}, mapping ...map[string]s
return doStruct(params, pointer, keyToAttributeNameMapping, "")
}
// doStructWithJsonCheck checks if given `params` is JSON, it then uses json.Unmarshal doing the converting.
func doStructWithJsonCheck(params interface{}, pointer interface{}) (err error, ok bool) {
switch r := params.(type) {
case []byte:
if json.Valid(r) {
if rv, ok := pointer.(reflect.Value); ok {
if rv.Kind() == reflect.Ptr {
return json.UnmarshalUseNumber(r, rv.Interface()), true
} else if rv.CanAddr() {
return json.UnmarshalUseNumber(r, rv.Addr().Interface()), true
}
} else {
return json.UnmarshalUseNumber(r, pointer), true
}
}
case string:
if paramsBytes := []byte(r); json.Valid(paramsBytes) {
if rv, ok := pointer.(reflect.Value); ok {
if rv.Kind() == reflect.Ptr {
return json.UnmarshalUseNumber(paramsBytes, rv.Interface()), true
} else if rv.CanAddr() {
return json.UnmarshalUseNumber(paramsBytes, rv.Addr().Interface()), true
}
} else {
return json.UnmarshalUseNumber(paramsBytes, pointer), true
}
}
default:
// The `params` might be struct that implements interface function Interface, eg: gvar.Var.
if v, ok := params.(apiInterface); ok {
return doStructWithJsonCheck(v.Interface(), pointer)
}
}
return nil, false
}
// doStruct is the core internal converting function for any data to struct.
func doStruct(params interface{}, pointer interface{}, mapping map[string]string, priorityTag string) (err error) {
if params == nil {
@ -63,47 +99,28 @@ func doStruct(params interface{}, pointer interface{}, mapping map[string]string
}
defer func() {
// Catch the panic, especially the reflect operation panics.
// Catch the panic, especially the reflection operation panics.
if exception := recover(); exception != nil {
if e, ok := exception.(errorStack); ok {
err = e
} else {
err = gerror.NewCodeSkipf(gcode.CodeInternalError, 1, "%v", exception)
err = gerror.NewCodeSkipf(gcode.CodeInternalError, 1, "%+v", exception)
}
}
}()
// If given `params` is JSON, it then uses json.Unmarshal doing the converting.
switch r := params.(type) {
case []byte:
if json.Valid(r) {
if rv, ok := pointer.(reflect.Value); ok {
if rv.Kind() == reflect.Ptr {
return json.UnmarshalUseNumber(r, rv.Interface())
} else if rv.CanAddr() {
return json.UnmarshalUseNumber(r, rv.Addr().Interface())
}
} else {
return json.UnmarshalUseNumber(r, pointer)
}
}
case string:
if paramsBytes := []byte(r); json.Valid(paramsBytes) {
if rv, ok := pointer.(reflect.Value); ok {
if rv.Kind() == reflect.Ptr {
return json.UnmarshalUseNumber(paramsBytes, rv.Interface())
} else if rv.CanAddr() {
return json.UnmarshalUseNumber(paramsBytes, rv.Addr().Interface())
}
} else {
return json.UnmarshalUseNumber(paramsBytes, pointer)
}
// JSON content converting.
err, ok := doStructWithJsonCheck(params, pointer)
if err != nil {
return err
}
if ok {
return nil
}
var (
paramsReflectValue reflect.Value
paramsInterface interface{} // DO NOT use `params` directly as it might be type of `reflect.Value`
paramsInterface interface{} // DO NOT use `params` directly as it might be type `reflect.Value`
pointerReflectValue reflect.Value
pointerReflectKind reflect.Kind
pointerElemReflectValue reflect.Value // The pointed element.
@ -129,6 +146,7 @@ func doStruct(params interface{}, pointer interface{}, mapping map[string]string
}
pointerElemReflectValue = pointerReflectValue.Elem()
}
// If `params` and `pointer` are the same type, the do directly assignment.
// For performance enhancement purpose.
if pointerElemReflectValue.IsValid() && pointerElemReflectValue.Type() == paramsReflectValue.Type() {
@ -284,7 +302,7 @@ func doStruct(params interface{}, pointer interface{}, mapping map[string]string
}
// Mark it done.
doneMap[attrName] = struct{}{}
if err := bindVarToStructAttr(pointerElemReflectValue, attrName, mapV, mapping, priorityTag); err != nil {
if err := bindVarToStructAttr(pointerElemReflectValue, attrName, mapV, mapping); err != nil {
return err
}
}
@ -292,7 +310,7 @@ func doStruct(params interface{}, pointer interface{}, mapping map[string]string
}
// bindVarToStructAttr sets value to struct object attribute by name.
func bindVarToStructAttr(elem reflect.Value, name string, value interface{}, mapping map[string]string, priorityTag string) (err error) {
func bindVarToStructAttr(elem reflect.Value, name string, value interface{}, mapping map[string]string) (err error) {
structFieldValue := elem.FieldByName(name)
if !structFieldValue.IsValid() {
return nil
@ -303,7 +321,7 @@ func bindVarToStructAttr(elem reflect.Value, name string, value interface{}, map
}
defer func() {
if exception := recover(); exception != nil {
if err = bindVarToReflectValue(structFieldValue, value, mapping, priorityTag); err != nil {
if err = bindVarToReflectValue(structFieldValue, value, mapping); err != nil {
err = gerror.WrapCodef(gcode.CodeInternalError, err, `error binding value to attribute "%s"`, name)
}
}
@ -323,7 +341,7 @@ func bindVarToStructAttr(elem reflect.Value, name string, value interface{}, map
return nil
}
// bindVarToReflectValueWithInterfaceCheck does binding using common interfaces checks.
// bindVarToReflectValueWithInterfaceCheck does bind using common interfaces checks.
func bindVarToReflectValueWithInterfaceCheck(reflectValue reflect.Value, value interface{}) (err error, ok bool) {
var pointer interface{}
if reflectValue.Kind() != reflect.Ptr && reflectValue.CanAddr() {
@ -384,10 +402,21 @@ func bindVarToReflectValueWithInterfaceCheck(reflectValue reflect.Value, value i
}
// bindVarToReflectValue sets `value` to reflect value object `structFieldValue`.
func bindVarToReflectValue(structFieldValue reflect.Value, value interface{}, mapping map[string]string, priorityTag string) (err error) {
func bindVarToReflectValue(structFieldValue reflect.Value, value interface{}, mapping map[string]string) (err error) {
// JSON content converting.
err, ok := doStructWithJsonCheck(value, structFieldValue)
if err != nil {
return err
}
if ok {
return nil
}
// Common interface check.
if err, ok := bindVarToReflectValueWithInterfaceCheck(structFieldValue, value); ok {
return err
}
kind := structFieldValue.Kind()
// Converting using interface, for some kinds.
switch kind {
@ -407,7 +436,7 @@ func bindVarToReflectValue(structFieldValue reflect.Value, value interface{}, ma
case reflect.Struct:
// Recursively converting for struct attribute.
if err := doStruct(value, structFieldValue, nil, ""); err != nil {
if err = doStruct(value, structFieldValue, nil, ""); err != nil {
// Note there's reflect conversion mechanism here.
structFieldValue.Set(reflect.ValueOf(value).Convert(structFieldValue.Type()))
}
@ -424,14 +453,14 @@ func bindVarToReflectValue(structFieldValue reflect.Value, value interface{}, ma
for i := 0; i < v.Len(); i++ {
if t.Kind() == reflect.Ptr {
e := reflect.New(t.Elem()).Elem()
if err := doStruct(v.Index(i).Interface(), e, nil, ""); err != nil {
if err = doStruct(v.Index(i).Interface(), e, nil, ""); err != nil {
// Note there's reflect conversion mechanism here.
e.Set(reflect.ValueOf(v.Index(i).Interface()).Convert(t))
}
a.Index(i).Set(e.Addr())
} else {
e := reflect.New(t).Elem()
if err := doStruct(v.Index(i).Interface(), e, nil, ""); err != nil {
if err = doStruct(v.Index(i).Interface(), e, nil, ""); err != nil {
// Note there's reflect conversion mechanism here.
e.Set(reflect.ValueOf(v.Index(i).Interface()).Convert(t))
}
@ -443,17 +472,18 @@ func bindVarToReflectValue(structFieldValue reflect.Value, value interface{}, ma
a = reflect.MakeSlice(structFieldValue.Type(), 1, 1)
t := a.Index(0).Type()
if t.Kind() == reflect.Ptr {
// Pointer element.
e := reflect.New(t.Elem()).Elem()
if err := doStruct(value, e, nil, ""); err != nil {
if err = doStruct(value, e, nil, ""); err != nil {
// Note there's reflect conversion mechanism here.
e.Set(reflect.ValueOf(value).Convert(t))
}
a.Index(0).Set(e.Addr())
} else {
// Just consider it as struct element. (Although it might be other types but not basic types, eg: map)
e := reflect.New(t).Elem()
if err := doStruct(value, e, nil, ""); err != nil {
// Note there's reflect conversion mechanism here.
e.Set(reflect.ValueOf(value).Convert(t))
if err = doStruct(value, e, nil, ""); err != nil {
return err
}
a.Index(0).Set(e)
}
@ -467,7 +497,7 @@ func bindVarToReflectValue(structFieldValue reflect.Value, value interface{}, ma
return err
}
elem := item.Elem()
if err = bindVarToReflectValue(elem, value, mapping, priorityTag); err == nil {
if err = bindVarToReflectValue(elem, value, mapping); err == nil {
structFieldValue.Set(elem.Addr())
}

View File

@ -62,7 +62,7 @@ func doStructs(params interface{}, pointer interface{}, mapping map[string]strin
if e, ok := exception.(errorStack); ok {
err = e
} else {
err = gerror.NewCodeSkipf(gcode.CodeInternalError, 1, "%v", exception)
err = gerror.NewCodeSkipf(gcode.CodeInternalError, 1, "%+v", exception)
}
}
}()

View File

@ -7,6 +7,7 @@
package gconv_test
import (
"github.com/gogf/gf/container/gvar"
"github.com/gogf/gf/frame/g"
"github.com/gogf/gf/test/gtest"
"github.com/gogf/gf/util/gconv"
@ -193,3 +194,84 @@ func Test_Scan_Maps(t *testing.T) {
})
})
}
func Test_Scan_JsonAttributes(t *testing.T) {
gtest.C(t, func(t *gtest.T) {
type Sku struct {
GiftId int64 `json:"gift_id"`
Name string `json:"name"`
ScorePrice int `json:"score_price"`
MarketPrice int `json:"market_price"`
CostPrice int `json:"cost_price"`
Stock int `json:"stock"`
}
v := gvar.New(`
[
{"name": "red", "stock": 10, "gift_id": 1, "cost_price": 80, "score_price": 188, "market_price": 188},
{"name": "blue", "stock": 100, "gift_id": 2, "cost_price": 81, "score_price": 200, "market_price": 288}
]`)
type Product struct {
Skus []Sku
}
var p *Product
err := gconv.Scan(g.Map{
"Skus": v,
}, &p)
t.AssertNil(err)
t.Assert(len(p.Skus), 2)
t.Assert(p.Skus[0].Name, "red")
t.Assert(p.Skus[0].Stock, 10)
t.Assert(p.Skus[0].GiftId, 1)
t.Assert(p.Skus[0].CostPrice, 80)
t.Assert(p.Skus[0].ScorePrice, 188)
t.Assert(p.Skus[0].MarketPrice, 188)
t.Assert(p.Skus[1].Name, "blue")
t.Assert(p.Skus[1].Stock, 100)
t.Assert(p.Skus[1].GiftId, 2)
t.Assert(p.Skus[1].CostPrice, 81)
t.Assert(p.Skus[1].ScorePrice, 200)
t.Assert(p.Skus[1].MarketPrice, 288)
})
}
func Test_Scan_JsonAttributes_StringArray(t *testing.T) {
gtest.C(t, func(t *gtest.T) {
type S struct {
Array []string
}
var s *S
err := gconv.Scan(g.Map{
"Array": `["a", "b"]`,
}, &s)
t.AssertNil(err)
t.Assert(len(s.Array), 2)
t.Assert(s.Array[0], "a")
t.Assert(s.Array[1], "b")
})
gtest.C(t, func(t *gtest.T) {
type S struct {
Array []string
}
var s *S
err := gconv.Scan(g.Map{
"Array": `[]`,
}, &s)
t.AssertNil(err)
t.Assert(len(s.Array), 0)
})
gtest.C(t, func(t *gtest.T) {
type S struct {
Array []int64
}
var s *S
err := gconv.Scan(g.Map{
"Array": `[]`,
}, &s)
t.AssertNil(err)
t.Assert(len(s.Array), 0)
})
}