fix(util/gconv): #3764 fix bool converting issue (#3765)

This commit is contained in:
wln32
2024-09-12 21:59:38 +08:00
committed by GitHub
parent 3d63ebfe81
commit 0e471eab38
11 changed files with 341 additions and 90 deletions

View File

@ -181,10 +181,6 @@ func String(any interface{}) string {
}
return value.String()
default:
// Empty checks.
if value == nil {
return ""
}
if f, ok := value.(localinterface.IString); ok {
// If the variable implements the String() interface,
// then use that interface to perform the conversion
@ -201,11 +197,11 @@ func String(any interface{}) string {
kind = rv.Kind()
)
switch kind {
case reflect.Chan,
case
reflect.Chan,
reflect.Map,
reflect.Slice,
reflect.Func,
reflect.Ptr,
reflect.Interface,
reflect.UnsafePointer:
if rv.IsNil() {
@ -213,9 +209,21 @@ func String(any interface{}) string {
}
case reflect.String:
return rv.String()
}
if kind == reflect.Ptr {
case reflect.Ptr:
if rv.IsNil() {
return ""
}
return String(rv.Elem().Interface())
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
return strconv.FormatInt(rv.Int(), 10)
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
return strconv.FormatUint(rv.Uint(), 10)
case reflect.Uintptr:
return strconv.FormatUint(rv.Uint(), 10)
case reflect.Float32, reflect.Float64:
return strconv.FormatFloat(rv.Float(), 'f', -1, 64)
case reflect.Bool:
return strconv.FormatBool(rv.Bool())
}
// Finally, we use json.Marshal to convert.
if jsonContent, err := json.Marshal(value); err != nil {
@ -252,10 +260,23 @@ func Bool(any interface{}) bool {
rv := reflect.ValueOf(any)
switch rv.Kind() {
case reflect.Ptr:
return !rv.IsNil()
case reflect.Map:
fallthrough
case reflect.Array:
if rv.IsNil() {
return false
}
if rv.Type().Elem().Kind() == reflect.Bool {
return rv.Elem().Bool()
}
return Bool(rv.Elem().Interface())
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
return rv.Int() != 0
case reflect.Uintptr, reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
return rv.Uint() != 0
case reflect.Float32, reflect.Float64:
return rv.Float() != 0
case reflect.Bool:
return rv.Bool()
// TODO(MapArraySliceStruct) It might panic here for these types.
case reflect.Map, reflect.Array:
fallthrough
case reflect.Slice:
return rv.Len() != 0

View File

@ -7,6 +7,7 @@
package gconv
import (
"reflect"
"strconv"
"github.com/gogf/gf/v2/encoding/gbinary"
@ -24,13 +25,40 @@ func Float32(any interface{}) float32 {
case float64:
return float32(value)
case []byte:
// TODO: It might panic here for these types.
return gbinary.DecodeToFloat32(value)
default:
if f, ok := value.(localinterface.IFloat32); ok {
return f.Float32()
rv := reflect.ValueOf(any)
switch rv.Kind() {
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
return float32(rv.Int())
case reflect.Uintptr, reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
return float32(rv.Uint())
case reflect.Float32, reflect.Float64:
return float32(rv.Float())
case reflect.Bool:
if rv.Bool() {
return 1
}
return 0
case reflect.String:
f, _ := strconv.ParseFloat(rv.String(), 32)
return float32(f)
case reflect.Ptr:
if rv.IsNil() {
return 0
}
if f, ok := value.(localinterface.IFloat32); ok {
return f.Float32()
}
return Float32(rv.Elem().Interface())
default:
if f, ok := value.(localinterface.IFloat32); ok {
return f.Float32()
}
v, _ := strconv.ParseFloat(String(any), 32)
return float32(v)
}
v, _ := strconv.ParseFloat(String(any), 64)
return float32(v)
}
}
@ -45,12 +73,44 @@ func Float64(any interface{}) float64 {
case float64:
return value
case []byte:
// TODO: It might panic here for these types.
return gbinary.DecodeToFloat64(value)
default:
if f, ok := value.(localinterface.IFloat64); ok {
return f.Float64()
rv := reflect.ValueOf(any)
switch rv.Kind() {
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
return float64(rv.Int())
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
return float64(rv.Uint())
case reflect.Uintptr:
return float64(rv.Uint())
case reflect.Float32, reflect.Float64:
// Please Note:
// When the type is float32 or a custom type defined based on float32,
// switching to float64 may result in a few extra decimal places.
return rv.Float()
case reflect.Bool:
if rv.Bool() {
return 1
}
return 0
case reflect.String:
f, _ := strconv.ParseFloat(rv.String(), 64)
return f
case reflect.Ptr:
if rv.IsNil() {
return 0
}
if f, ok := value.(localinterface.IFloat64); ok {
return f.Float64()
}
return Float64(rv.Elem().Interface())
default:
if f, ok := value.(localinterface.IFloat64); ok {
return f.Float64()
}
v, _ := strconv.ParseFloat(String(any), 64)
return v
}
v, _ := strconv.ParseFloat(String(any), 64)
return v
}
}

View File

@ -8,6 +8,7 @@ package gconv
import (
"math"
"reflect"
"strconv"
"github.com/gogf/gf/v2/encoding/gbinary"
@ -63,44 +64,37 @@ func Int64(any interface{}) int64 {
if any == nil {
return 0
}
switch value := any.(type) {
case int:
return int64(value)
case int8:
return int64(value)
case int16:
return int64(value)
case int32:
return int64(value)
case int64:
return value
case uint:
return int64(value)
case uint8:
return int64(value)
case uint16:
return int64(value)
case uint32:
return int64(value)
case uint64:
return int64(value)
case float32:
return int64(value)
case float64:
return int64(value)
case bool:
if value {
rv := reflect.ValueOf(any)
switch rv.Kind() {
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
return int64(rv.Int())
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
return int64(rv.Uint())
case reflect.Uintptr:
return int64(rv.Uint())
case reflect.Float32, reflect.Float64:
return int64(rv.Float())
case reflect.Bool:
if rv.Bool() {
return 1
}
return 0
case []byte:
return gbinary.DecodeToInt64(value)
default:
if f, ok := value.(localinterface.IInt64); ok {
case reflect.Ptr:
if rv.IsNil() {
return 0
}
if f, ok := any.(localinterface.IInt64); ok {
return f.Int64()
}
return Int64(rv.Elem().Interface())
case reflect.Slice:
// TODO: It might panic here for these types.
if rv.Type().Elem().Kind() == reflect.Uint8 {
return gbinary.DecodeToInt64(rv.Bytes())
}
case reflect.String:
var (
s = String(value)
s = rv.String()
isMinus = false
)
if len(s) > 0 {
@ -111,7 +105,7 @@ func Int64(any interface{}) int64 {
s = s[1:]
}
}
// Hexadecimal
// Hexadecimal.
if len(s) > 2 && s[0] == '0' && (s[1] == 'x' || s[1] == 'X') {
if v, e := strconv.ParseInt(s[2:], 16, 64); e == nil {
if isMinus {
@ -120,18 +114,23 @@ func Int64(any interface{}) int64 {
return v
}
}
// Decimal
// Decimal.
if v, e := strconv.ParseInt(s, 10, 64); e == nil {
if isMinus {
return -v
}
return v
}
// Float64
if valueInt64 := Float64(value); math.IsNaN(valueInt64) {
// Float64.
if valueInt64 := Float64(s); math.IsNaN(valueInt64) {
return 0
} else {
return int64(valueInt64)
}
default:
if f, ok := any.(localinterface.IInt64); ok {
return f.Int64()
}
}
return 0
}

View File

@ -8,6 +8,7 @@ package gconv
import (
"math"
"reflect"
"strconv"
"github.com/gogf/gf/v2/encoding/gbinary"
@ -63,43 +64,38 @@ func Uint64(any interface{}) uint64 {
if any == nil {
return 0
}
switch value := any.(type) {
case int:
return uint64(value)
case int8:
return uint64(value)
case int16:
return uint64(value)
case int32:
return uint64(value)
case int64:
return uint64(value)
case uint:
return uint64(value)
case uint8:
return uint64(value)
case uint16:
return uint64(value)
case uint32:
return uint64(value)
case uint64:
return value
case float32:
return uint64(value)
case float64:
return uint64(value)
case bool:
if value {
rv := reflect.ValueOf(any)
switch rv.Kind() {
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
return uint64(rv.Int())
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
return uint64(rv.Uint())
case reflect.Uintptr:
return uint64(rv.Uint())
case reflect.Float32, reflect.Float64:
return uint64(rv.Float())
case reflect.Bool:
if rv.Bool() {
return 1
}
return 0
case []byte:
return gbinary.DecodeToUint64(value)
default:
if f, ok := value.(localinterface.IUint64); ok {
case reflect.Ptr:
if rv.IsNil() {
return 0
}
if f, ok := any.(localinterface.IUint64); ok {
return f.Uint64()
}
s := String(value)
return Uint64(rv.Elem().Interface())
case reflect.Slice:
// TODOThese types should be panic
if rv.Type().Elem().Kind() == reflect.Uint8 {
return gbinary.DecodeToUint64(rv.Bytes())
}
case reflect.String:
var (
s = rv.String()
)
// Hexadecimal
if len(s) > 2 && s[0] == '0' && (s[1] == 'x' || s[1] == 'X') {
if v, e := strconv.ParseUint(s[2:], 16, 64); e == nil {
@ -111,10 +107,15 @@ func Uint64(any interface{}) uint64 {
return v
}
// Float64
if valueFloat64 := Float64(value); math.IsNaN(valueFloat64) {
if valueFloat64 := Float64(any); math.IsNaN(valueFloat64) {
return 0
} else {
return uint64(valueFloat64)
}
default:
if f, ok := any.(localinterface.IUint64); ok {
return f.Uint64()
}
}
return 0
}

View File

@ -13,6 +13,11 @@ import (
"github.com/gogf/gf/v2/util/gconv"
)
var (
boolTestTrueValue = true
boolTestFalseValue = false
)
var boolTests = []struct {
value interface{}
expect bool
@ -46,6 +51,17 @@ var boolTests = []struct {
{struct{}{}, true},
{&struct{}{}, true},
{nil, false},
{(*bool)(nil), false},
{&boolTestTrueValue, true},
{&boolTestFalseValue, false},
{myBool(true), true},
{myBool(false), false},
{(*myBool)(&boolTestTrueValue), true},
{(*myBool)(&boolTestFalseValue), false},
{(*myBool)(nil), false},
}
func TestBool(t *testing.T) {

View File

@ -0,0 +1,28 @@
// Copyright GoFrame Author(https://goframe.org). 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
type (
myInt int
myInt8 int8
myInt16 int16
myInt32 int32
myInt64 int64
myUint uint
myUint8 uint8
myUint16 uint16
myUint32 uint32
myUint64 uint64
myFloat32 float32
myFloat64 float64
myBool bool
myString string
)

View File

@ -16,12 +16,20 @@ import (
"github.com/gogf/gf/v2/util/gconv"
)
var (
// Please Note:
// When the type is float32 or a custom type defined based on float32,
// switching to float64 may result in a few extra decimal places.
float32TestValue = float32(123)
float64TestValue = float64(123.456)
)
var floatTests = []struct {
value interface{}
expect32 float32
expect64 float64
}{
{true, 0, 0},
{true, 1, 1},
{false, 0, 0},
{int(0), 0, 0},
@ -70,9 +78,23 @@ var floatTests = []struct {
{struct{}{}, 0, 0},
{nil, 0, 0},
{(*float32)(nil), 0, 0},
{(*float64)(nil), 0, 0},
{gvar.New(123), 123, 123},
{gvar.New(123.456), 123.456, 123.456},
{&float32TestValue, 123, 123},
{&float64TestValue, 123.456, 123.456},
{myFloat32(123), 123, 123},
{myFloat64(123.456), 123.456, 123.456},
{(*myFloat32)(&float32TestValue), 123, 123},
{(*myFloat64)(&float64TestValue), 123.456, 123.456},
{(*myFloat32)(nil), 0, 0},
{(*myFloat64)(nil), 0, 0},
}
func TestFloat32(t *testing.T) {

View File

@ -15,6 +15,14 @@ import (
"github.com/gogf/gf/v2/util/gconv"
)
var (
intTestValue = 123
int8TestValue = int8(123)
int16TestValue = int16(123)
int32TestValue = int32(123)
int64TestValue = int64(123)
)
var intTests = []struct {
value interface{}
expect int
@ -79,8 +87,38 @@ var intTests = []struct {
{struct{}{}, 0, 0, 0, 0, 0},
{nil, 0, 0, 0, 0, 0},
{(*int)(nil), 0, 0, 0, 0, 0},
{(*int8)(nil), 0, 0, 0, 0, 0},
{(*int16)(nil), 0, 0, 0, 0, 0},
{(*int32)(nil), 0, 0, 0, 0, 0},
{(*int64)(nil), 0, 0, 0, 0, 0},
{gvar.New(123), 123, 123, 123, 123, 123},
{gvar.New(123.456), 123, 123, 123, 123, 123},
{&intTestValue, 123, 123, 123, 123, 123},
{&int8TestValue, 123, 123, 123, 123, 123},
{&int16TestValue, 123, 123, 123, 123, 123},
{&int32TestValue, 123, 123, 123, 123, 123},
{&int64TestValue, 123, 123, 123, 123, 123},
{(myInt)(intTestValue), 123, 123, 123, 123, 123},
{(myInt8)(int8TestValue), 123, 123, 123, 123, 123},
{(myInt16)(int16TestValue), 123, 123, 123, 123, 123},
{(myInt32)(int32TestValue), 123, 123, 123, 123, 123},
{(myInt64)(int64TestValue), 123, 123, 123, 123, 123},
{(*myInt)(&intTestValue), 123, 123, 123, 123, 123},
{(*myInt8)(&int8TestValue), 123, 123, 123, 123, 123},
{(*myInt16)(&int16TestValue), 123, 123, 123, 123, 123},
{(*myInt32)(&int32TestValue), 123, 123, 123, 123, 123},
{(*myInt64)(&int64TestValue), 123, 123, 123, 123, 123},
{(*myInt)(nil), 0, 0, 0, 0, 0},
{(*myInt8)(nil), 0, 0, 0, 0, 0},
{(*myInt16)(nil), 0, 0, 0, 0, 0},
{(*myInt32)(nil), 0, 0, 0, 0, 0},
{(*myInt64)(nil), 0, 0, 0, 0, 0},
}
func TestInt(t *testing.T) {

View File

@ -387,3 +387,30 @@ func TestIssue3731(t *testing.T) {
t.AssertEQ("<nil>", fmt.Sprintf("%T", args.Doc["craft"]))
})
}
// https://github.com/gogf/gf/issues/3764
func TestIssue3764(t *testing.T) {
type T struct {
True bool `json:"true"`
False bool `json:"false"`
TruePtr *bool `json:"true_ptr"`
FalsePtr *bool `json:"false_ptr"`
}
gtest.C(t, func(t *gtest.T) {
trueValue := true
falseValue := false
m := g.Map{
"true": trueValue,
"false": falseValue,
"true_ptr": &trueValue,
"false_ptr": &falseValue,
}
tt := &T{}
err := gconv.Struct(m, &tt)
t.AssertNil(err)
t.AssertEQ(tt.True, true)
t.AssertEQ(tt.False, false)
t.AssertEQ(*tt.TruePtr, trueValue)
t.AssertEQ(*tt.FalsePtr, falseValue)
})
}

View File

@ -65,6 +65,7 @@ var stringTests = []struct {
{struct{}{}, "{}"},
{nil, ""},
{(*string)(nil), ""},
{gvar.New(123), "123"},
{gvar.New(123.456), "123.456"},

View File

@ -15,6 +15,14 @@ import (
"github.com/gogf/gf/v2/util/gconv"
)
var (
uintTestValue = uint(123)
uint8TestValue = uint8(123)
uint16TestValue = uint16(123)
uint32TestValue = uint32(123)
uint64TestValue = uint64(123)
)
var uintTests = []struct {
value interface{}
expect uint
@ -76,8 +84,38 @@ var uintTests = []struct {
{struct{}{}, 0, 0, 0, 0, 0},
{nil, 0, 0, 0, 0, 0},
{(*uint)(nil), 0, 0, 0, 0, 0},
{(*uint8)(nil), 0, 0, 0, 0, 0},
{(*uint16)(nil), 0, 0, 0, 0, 0},
{(*uint32)(nil), 0, 0, 0, 0, 0},
{(*uint64)(nil), 0, 0, 0, 0, 0},
{gvar.New(123), 123, 123, 123, 123, 123},
{gvar.New(123.456), 123, 123, 123, 123, 123},
{&uintTestValue, 123, 123, 123, 123, 123},
{&uint8TestValue, 123, 123, 123, 123, 123},
{&uint16TestValue, 123, 123, 123, 123, 123},
{&uint32TestValue, 123, 123, 123, 123, 123},
{&uint64TestValue, 123, 123, 123, 123, 123},
{(myUint)(uintTestValue), 123, 123, 123, 123, 123},
{(myUint8)(uint8TestValue), 123, 123, 123, 123, 123},
{(myUint16)(uint16TestValue), 123, 123, 123, 123, 123},
{(myUint32)(uint32TestValue), 123, 123, 123, 123, 123},
{(myUint64)(uint64TestValue), 123, 123, 123, 123, 123},
{(*myUint)(&uintTestValue), 123, 123, 123, 123, 123},
{(*myUint8)(&uint8TestValue), 123, 123, 123, 123, 123},
{(*myUint16)(&uint16TestValue), 123, 123, 123, 123, 123},
{(*myUint32)(&uint32TestValue), 123, 123, 123, 123, 123},
{(*myUint64)(&uint64TestValue), 123, 123, 123, 123, 123},
{(*myUint)(nil), 0, 0, 0, 0, 0},
{(*myUint8)(nil), 0, 0, 0, 0, 0},
{(*myUint16)(nil), 0, 0, 0, 0, 0},
{(*myUint32)(nil), 0, 0, 0, 0, 0},
{(*myUint64)(nil), 0, 0, 0, 0, 0},
}
func TestUint(t *testing.T) {