mirror of
https://gitee.com/johng/gf
synced 2026-07-04 21:03:13 +08:00
Merge branch 'gogf:master' into master
This commit is contained in:
@ -12,6 +12,8 @@ import (
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/gogf/gf/v2/errors/gcode"
|
||||
"github.com/gogf/gf/v2/errors/gerror"
|
||||
"github.com/gogf/gf/v2/internal/rwmutex"
|
||||
"github.com/gogf/gf/v2/internal/utils"
|
||||
"github.com/gogf/gf/v2/text/gstr"
|
||||
@ -38,9 +40,14 @@ type Options struct {
|
||||
StrNumber bool // StrNumber causes the Decoder to unmarshal a number into an interface{} as a string instead of as a float64.
|
||||
}
|
||||
|
||||
// iInterface is used for type assert api for Interface().
|
||||
type iInterface interface {
|
||||
Interface() interface{}
|
||||
// iInterfaces is used for type assert api for Interfaces().
|
||||
type iInterfaces interface {
|
||||
Interfaces() []interface{}
|
||||
}
|
||||
|
||||
// iMapStrAny is the interface support for converting struct parameter to map.
|
||||
type iMapStrAny interface {
|
||||
MapStrAny() map[string]interface{}
|
||||
}
|
||||
|
||||
// setValue sets `value` to `j` by `pattern`.
|
||||
@ -48,16 +55,14 @@ type iInterface interface {
|
||||
// 1. If value is nil and removed is true, means deleting this value;
|
||||
// 2. It's quite complicated in hierarchical data search, node creating and data assignment;
|
||||
func (j *Json) setValue(pattern string, value interface{}, removed bool) error {
|
||||
if value != nil {
|
||||
if utils.IsStruct(value) {
|
||||
if v, ok := value.(iInterface); ok {
|
||||
value = v.Interface()
|
||||
}
|
||||
}
|
||||
var (
|
||||
err error
|
||||
array = strings.Split(pattern, string(j.c))
|
||||
length = len(array)
|
||||
)
|
||||
if value, err = j.convertValue(value); err != nil {
|
||||
return gerror.Wrap(err, `Json Set failed`)
|
||||
}
|
||||
array := strings.Split(pattern, string(j.c))
|
||||
length := len(array)
|
||||
value = j.convertValue(value)
|
||||
// Initialization checks.
|
||||
if *j.p == nil {
|
||||
if gstr.IsNumeric(array[0]) {
|
||||
@ -252,30 +257,51 @@ done:
|
||||
|
||||
// convertValue converts `value` to map[string]interface{} or []interface{},
|
||||
// which can be supported for hierarchical data access.
|
||||
func (j *Json) convertValue(value interface{}) interface{} {
|
||||
func (j *Json) convertValue(value interface{}) (convertedValue interface{}, err error) {
|
||||
if value == nil {
|
||||
return
|
||||
}
|
||||
|
||||
switch value.(type) {
|
||||
case map[string]interface{}:
|
||||
return value
|
||||
return value, nil
|
||||
|
||||
case []interface{}:
|
||||
return value
|
||||
return value, nil
|
||||
|
||||
default:
|
||||
var (
|
||||
reflectInfo = utils.OriginValueAndKind(value)
|
||||
)
|
||||
switch reflectInfo.OriginKind {
|
||||
case reflect.Array:
|
||||
return gconv.Interfaces(value)
|
||||
return gconv.Interfaces(value), nil
|
||||
|
||||
case reflect.Slice:
|
||||
return gconv.Interfaces(value)
|
||||
return gconv.Interfaces(value), nil
|
||||
|
||||
case reflect.Map:
|
||||
return gconv.Map(value)
|
||||
return gconv.Map(value), nil
|
||||
|
||||
case reflect.Struct:
|
||||
return gconv.Map(value)
|
||||
if v, ok := value.(iMapStrAny); ok {
|
||||
convertedValue = v.MapStrAny()
|
||||
}
|
||||
if utils.IsNil(convertedValue) {
|
||||
if v, ok := value.(iInterfaces); ok {
|
||||
convertedValue = v.Interfaces()
|
||||
}
|
||||
}
|
||||
if utils.IsNil(convertedValue) {
|
||||
convertedValue = gconv.Map(value)
|
||||
}
|
||||
if utils.IsNil(convertedValue) {
|
||||
err = gerror.NewCodef(gcode.CodeInvalidParameter, `unsupported value type "%s"`, reflect.TypeOf(value))
|
||||
}
|
||||
return
|
||||
|
||||
default:
|
||||
// Use json decode/encode at last.
|
||||
b, _ := Encode(value)
|
||||
v, _ := Decode(b)
|
||||
return v
|
||||
return value, nil
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -106,12 +106,26 @@ func (j *Json) Set(pattern string, value interface{}) error {
|
||||
return j.setValue(pattern, value, false)
|
||||
}
|
||||
|
||||
// MustSet performs as Set, but it panics if any error occurs.
|
||||
func (j *Json) MustSet(pattern string, value interface{}) {
|
||||
if err := j.Set(pattern, value); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
}
|
||||
|
||||
// Remove deletes value with specified `pattern`.
|
||||
// It supports hierarchical data access by char separator, which is '.' in default.
|
||||
func (j *Json) Remove(pattern string) error {
|
||||
return j.setValue(pattern, nil, true)
|
||||
}
|
||||
|
||||
// MustRemove performs as Remove, but it panics if any error occurs.
|
||||
func (j *Json) MustRemove(pattern string) {
|
||||
if err := j.Remove(pattern); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
}
|
||||
|
||||
// Contains checks whether the value by specified `pattern` exist.
|
||||
func (j *Json) Contains(pattern string) bool {
|
||||
return j.Get(pattern) != nil
|
||||
@ -155,6 +169,13 @@ func (j *Json) Append(pattern string, value interface{}) error {
|
||||
return gerror.NewCodef(gcode.CodeInvalidParameter, "invalid variable type of %s", pattern)
|
||||
}
|
||||
|
||||
// MustAppend performs as Append, but it panics if any error occurs.
|
||||
func (j *Json) MustAppend(pattern string, value interface{}) {
|
||||
if err := j.Append(pattern, value); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
}
|
||||
|
||||
// Map converts current Json object to map[string]interface{}.
|
||||
// It returns nil if fails.
|
||||
func (j *Json) Map() map[string]interface{} {
|
||||
|
||||
@ -29,3 +29,13 @@ func (j *Json) UnmarshalValue(value interface{}) error {
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// MapStrAny implements interface function MapStrAny().
|
||||
func (j *Json) MapStrAny() map[string]interface{} {
|
||||
return j.Map()
|
||||
}
|
||||
|
||||
// Interfaces implements interface function Interfaces().
|
||||
func (j *Json) Interfaces() []interface{} {
|
||||
return j.Array()
|
||||
}
|
||||
|
||||
@ -10,6 +10,7 @@ import (
|
||||
"bytes"
|
||||
"testing"
|
||||
|
||||
"github.com/gogf/gf/v2/container/garray"
|
||||
"github.com/gogf/gf/v2/encoding/gjson"
|
||||
"github.com/gogf/gf/v2/frame/g"
|
||||
"github.com/gogf/gf/v2/test/gtest"
|
||||
@ -329,3 +330,12 @@ func Test_Set20(t *testing.T) {
|
||||
), true)
|
||||
})
|
||||
}
|
||||
|
||||
func Test_Set_GArray(t *testing.T) {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
j := gjson.New(nil)
|
||||
arr := garray.New().Append("test")
|
||||
t.AssertNil(j.Set("arr", arr))
|
||||
t.Assert(j.Get("arr").Array(), g.Slice{"test"})
|
||||
})
|
||||
}
|
||||
|
||||
@ -510,8 +510,8 @@ func TestJson_Set_With_Struct(t *testing.T) {
|
||||
"user3": g.Map{"name": "user3"},
|
||||
})
|
||||
user1 := v.GetJson("user1")
|
||||
user1.Set("id", 111)
|
||||
v.Set("user1", user1)
|
||||
t.AssertNil(user1.Set("id", 111))
|
||||
t.AssertNil(v.Set("user1", user1))
|
||||
t.Assert(v.Get("user1.id"), 111)
|
||||
})
|
||||
}
|
||||
|
||||
@ -84,8 +84,8 @@ func NewSkipf(skip int, format string, args ...interface{}) error {
|
||||
}
|
||||
}
|
||||
|
||||
// Wrap wraps error with text.
|
||||
// It returns nil if given err is nil.
|
||||
// Wrap wraps error with text. It returns nil if given err is nil.
|
||||
// Note that it does not lose the error code of wrapped error, as it inherits the error code from it.
|
||||
func Wrap(err error, text string) error {
|
||||
if err == nil {
|
||||
return nil
|
||||
@ -98,9 +98,9 @@ func Wrap(err error, text string) error {
|
||||
}
|
||||
}
|
||||
|
||||
// Wrapf returns an error annotating err with a stack trace
|
||||
// at the point Wrapf is called, and the format specifier.
|
||||
// Wrapf returns an error annotating err with a stack trace at the point Wrapf is called, and the format specifier.
|
||||
// It returns nil if given `err` is nil.
|
||||
// Note that it does not lose the error code of wrapped error, as it inherits the error code from it.
|
||||
func Wrapf(err error, format string, args ...interface{}) error {
|
||||
if err == nil {
|
||||
return nil
|
||||
@ -113,9 +113,9 @@ func Wrapf(err error, format string, args ...interface{}) error {
|
||||
}
|
||||
}
|
||||
|
||||
// WrapSkip wraps error with text.
|
||||
// It returns nil if given err is nil.
|
||||
// WrapSkip wraps error with text. It returns nil if given err is nil.
|
||||
// The parameter `skip` specifies the stack callers skipped amount.
|
||||
// Note that it does not lose the error code of wrapped error, as it inherits the error code from it.
|
||||
func WrapSkip(skip int, err error, text string) error {
|
||||
if err == nil {
|
||||
return nil
|
||||
@ -128,9 +128,9 @@ func WrapSkip(skip int, err error, text string) error {
|
||||
}
|
||||
}
|
||||
|
||||
// WrapSkipf wraps error with text that is formatted with given format and args.
|
||||
// It returns nil if given err is nil.
|
||||
// WrapSkipf wraps error with text that is formatted with given format and args. It returns nil if given err is nil.
|
||||
// The parameter `skip` specifies the stack callers skipped amount.
|
||||
// Note that it does not lose the error code of wrapped error, as it inherits the error code from it.
|
||||
func WrapSkipf(skip int, err error, format string, args ...interface{}) error {
|
||||
if err == nil {
|
||||
return nil
|
||||
|
||||
@ -298,7 +298,7 @@ func IsEmpty(value interface{}) bool {
|
||||
// return false
|
||||
//}
|
||||
|
||||
// IsNil checks whether given `value` is nil.
|
||||
// IsNil checks whether given `value` is nil, especially for interface{} type value.
|
||||
// Parameter `traceSource` is used for tracing to the source variable if given `value` is type of pinter
|
||||
// that also points to a pointer. It returns nil if the source is nil when `traceSource` is true.
|
||||
// Note that it might use reflect feature which affects performance a little.
|
||||
|
||||
@ -12,7 +12,7 @@ import (
|
||||
"github.com/gogf/gf/v2/internal/empty"
|
||||
)
|
||||
|
||||
// IsNil checks whether `value` is nil.
|
||||
// IsNil checks whether `value` is nil, especially for interface{} type value.
|
||||
func IsNil(value interface{}) bool {
|
||||
return empty.IsNil(value)
|
||||
}
|
||||
|
||||
@ -69,7 +69,7 @@ func CaseSnakeFirstUpper(word string, underscore ...string) string {
|
||||
}
|
||||
|
||||
for {
|
||||
m := firstCamelCaseStart.FindAllStringSubmatch(word, 1)
|
||||
m = firstCamelCaseStart.FindAllStringSubmatch(word, 1)
|
||||
if len(m) > 0 && m[0][1] != "" {
|
||||
w := strings.ToLower(m[0][1])
|
||||
w = w[:len(w)-1] + replace + string(w[len(w)-1])
|
||||
|
||||
@ -7,8 +7,9 @@
|
||||
package gstr
|
||||
|
||||
import (
|
||||
"github.com/gogf/gf/v2/internal/utils"
|
||||
"strings"
|
||||
|
||||
"github.com/gogf/gf/v2/internal/utils"
|
||||
)
|
||||
|
||||
// Replace returns a copy of the string `origin`
|
||||
@ -32,13 +33,21 @@ func ReplaceI(origin, search, replace string, count ...int) string {
|
||||
return origin
|
||||
}
|
||||
var (
|
||||
length = len(search)
|
||||
searchLower = strings.ToLower(search)
|
||||
searchLength = len(search)
|
||||
searchLower = strings.ToLower(search)
|
||||
originLower string
|
||||
pos int
|
||||
diff = len(replace) - searchLength
|
||||
)
|
||||
for {
|
||||
originLower := strings.ToLower(origin)
|
||||
if pos := strings.Index(originLower, searchLower); pos != -1 {
|
||||
origin = origin[:pos] + replace + origin[pos+length:]
|
||||
originLower = strings.ToLower(origin)
|
||||
if pos = Pos(originLower, searchLower, pos); pos != -1 {
|
||||
origin = origin[:pos] + replace + origin[pos+searchLength:]
|
||||
if diff < 0 {
|
||||
pos += -diff
|
||||
} else {
|
||||
pos += diff + 1
|
||||
}
|
||||
if n--; n == 0 {
|
||||
break
|
||||
}
|
||||
|
||||
90
text/gstr/gstr_z_unit_replace_test.go
Normal file
90
text/gstr/gstr_z_unit_replace_test.go
Normal file
@ -0,0 +1,90 @@
|
||||
// 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.
|
||||
|
||||
// go test *.go -bench=".*"
|
||||
|
||||
package gstr_test
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/gogf/gf/v2/frame/g"
|
||||
"github.com/gogf/gf/v2/test/gtest"
|
||||
"github.com/gogf/gf/v2/text/gstr"
|
||||
)
|
||||
|
||||
func Test_Replace(t *testing.T) {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
s1 := "abcdEFG乱入的中文abcdefg"
|
||||
t.Assert(gstr.Replace(s1, "ab", "AB"), "ABcdEFG乱入的中文ABcdefg")
|
||||
t.Assert(gstr.Replace(s1, "EF", "ef"), "abcdefG乱入的中文abcdefg")
|
||||
t.Assert(gstr.Replace(s1, "MN", "mn"), s1)
|
||||
|
||||
t.Assert(gstr.ReplaceByArray(s1, g.ArrayStr{
|
||||
"a", "A",
|
||||
"A", "-",
|
||||
"a",
|
||||
}), "-bcdEFG乱入的中文-bcdefg")
|
||||
|
||||
t.Assert(gstr.ReplaceByMap(s1, g.MapStrStr{
|
||||
"a": "A",
|
||||
"G": "g",
|
||||
}), "AbcdEFg乱入的中文Abcdefg")
|
||||
})
|
||||
}
|
||||
|
||||
func Test_ReplaceI_1(t *testing.T) {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
s1 := "abcd乱入的中文ABCD"
|
||||
s2 := "a"
|
||||
t.Assert(gstr.ReplaceI(s1, "ab", "aa"), "aacd乱入的中文aaCD")
|
||||
t.Assert(gstr.ReplaceI(s1, "ab", "aa", 0), "abcd乱入的中文ABCD")
|
||||
t.Assert(gstr.ReplaceI(s1, "ab", "aa", 1), "aacd乱入的中文ABCD")
|
||||
|
||||
t.Assert(gstr.ReplaceI(s1, "abcd", "-"), "-乱入的中文-")
|
||||
t.Assert(gstr.ReplaceI(s1, "abcd", "-", 1), "-乱入的中文ABCD")
|
||||
|
||||
t.Assert(gstr.ReplaceI(s1, "abcd乱入的", ""), "中文ABCD")
|
||||
t.Assert(gstr.ReplaceI(s1, "ABCD乱入的", ""), "中文ABCD")
|
||||
|
||||
t.Assert(gstr.ReplaceI(s2, "A", "-"), "-")
|
||||
t.Assert(gstr.ReplaceI(s2, "a", "-"), "-")
|
||||
|
||||
t.Assert(gstr.ReplaceIByArray(s1, g.ArrayStr{
|
||||
"abcd乱入的", "-",
|
||||
"-", "=",
|
||||
"a",
|
||||
}), "=中文ABCD")
|
||||
|
||||
t.Assert(gstr.ReplaceIByMap(s1, g.MapStrStr{
|
||||
"ab": "-",
|
||||
"CD": "=",
|
||||
}), "-=乱入的中文-=")
|
||||
})
|
||||
}
|
||||
|
||||
func Test_ReplaceI_2(t *testing.T) {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
t.Assert(gstr.ReplaceI("aaa", "A", "-a-"), `-a--a--a-`)
|
||||
t.Assert(gstr.ReplaceI("aaaa", "AA", "-"), `--`)
|
||||
t.Assert(gstr.ReplaceI("a a a", "A", "b"), `b b b`)
|
||||
t.Assert(gstr.ReplaceI("aaaaaa", "aa", "a"), `aaa`)
|
||||
t.Assert(gstr.ReplaceI("aaaaaa", "AA", "A"), `AAA`)
|
||||
t.Assert(gstr.ReplaceI("aaa", "A", "AA"), `AAAAAA`)
|
||||
t.Assert(gstr.ReplaceI("aaa", "A", "AA"), `AAAAAA`)
|
||||
t.Assert(gstr.ReplaceI("a duration", "duration", "recordduration"), `a recordduration`)
|
||||
})
|
||||
// With count parameter.
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
t.Assert(gstr.ReplaceI("aaaaaa", "aa", "a", 2), `aaaa`)
|
||||
t.Assert(gstr.ReplaceI("aaaaaa", "AA", "A", 1), `Aaaaa`)
|
||||
t.Assert(gstr.ReplaceI("aaaaaa", "AA", "A", 3), `AAA`)
|
||||
t.Assert(gstr.ReplaceI("aaaaaa", "AA", "A", 4), `AAA`)
|
||||
t.Assert(gstr.ReplaceI("aaa", "A", "AA", 2), `AAAAa`)
|
||||
t.Assert(gstr.ReplaceI("aaa", "A", "AA", 3), `AAAAAA`)
|
||||
t.Assert(gstr.ReplaceI("aaa", "A", "AA", 4), `AAAAAA`)
|
||||
})
|
||||
}
|
||||
@ -11,61 +11,10 @@ package gstr_test
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/gogf/gf/v2/frame/g"
|
||||
"github.com/gogf/gf/v2/test/gtest"
|
||||
"github.com/gogf/gf/v2/text/gstr"
|
||||
)
|
||||
|
||||
func Test_Replace(t *testing.T) {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
s1 := "abcdEFG乱入的中文abcdefg"
|
||||
t.Assert(gstr.Replace(s1, "ab", "AB"), "ABcdEFG乱入的中文ABcdefg")
|
||||
t.Assert(gstr.Replace(s1, "EF", "ef"), "abcdefG乱入的中文abcdefg")
|
||||
t.Assert(gstr.Replace(s1, "MN", "mn"), s1)
|
||||
|
||||
t.Assert(gstr.ReplaceByArray(s1, g.ArrayStr{
|
||||
"a", "A",
|
||||
"A", "-",
|
||||
"a",
|
||||
}), "-bcdEFG乱入的中文-bcdefg")
|
||||
|
||||
t.Assert(gstr.ReplaceByMap(s1, g.MapStrStr{
|
||||
"a": "A",
|
||||
"G": "g",
|
||||
}), "AbcdEFg乱入的中文Abcdefg")
|
||||
})
|
||||
}
|
||||
|
||||
func Test_ReplaceI_1(t *testing.T) {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
s1 := "abcd乱入的中文ABCD"
|
||||
s2 := "a"
|
||||
t.Assert(gstr.ReplaceI(s1, "ab", "aa"), "aacd乱入的中文aaCD")
|
||||
t.Assert(gstr.ReplaceI(s1, "ab", "aa", 0), "abcd乱入的中文ABCD")
|
||||
t.Assert(gstr.ReplaceI(s1, "ab", "aa", 1), "aacd乱入的中文ABCD")
|
||||
|
||||
t.Assert(gstr.ReplaceI(s1, "abcd", "-"), "-乱入的中文-")
|
||||
t.Assert(gstr.ReplaceI(s1, "abcd", "-", 1), "-乱入的中文ABCD")
|
||||
|
||||
t.Assert(gstr.ReplaceI(s1, "abcd乱入的", ""), "中文ABCD")
|
||||
t.Assert(gstr.ReplaceI(s1, "ABCD乱入的", ""), "中文ABCD")
|
||||
|
||||
t.Assert(gstr.ReplaceI(s2, "A", "-"), "-")
|
||||
t.Assert(gstr.ReplaceI(s2, "a", "-"), "-")
|
||||
|
||||
t.Assert(gstr.ReplaceIByArray(s1, g.ArrayStr{
|
||||
"abcd乱入的", "-",
|
||||
"-", "=",
|
||||
"a",
|
||||
}), "=中文ABCD")
|
||||
|
||||
t.Assert(gstr.ReplaceIByMap(s1, g.MapStrStr{
|
||||
"ab": "-",
|
||||
"CD": "=",
|
||||
}), "-=乱入的中文-=")
|
||||
})
|
||||
}
|
||||
|
||||
func Test_ToLower(t *testing.T) {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
s1 := "abcdEFG乱入的中文abcdefg"
|
||||
@ -306,8 +306,10 @@ func Bytes(any interface{}) []byte {
|
||||
switch value := any.(type) {
|
||||
case string:
|
||||
return []byte(value)
|
||||
|
||||
case []byte:
|
||||
return value
|
||||
|
||||
default:
|
||||
if f, ok := value.(iBytes); ok {
|
||||
return f.Bytes()
|
||||
|
||||
Reference in New Issue
Block a user