mirror of
https://gitee.com/johng/gf.git
synced 2024-11-29 18:57:44 +08:00
improve package gjson
This commit is contained in:
parent
de17302ad0
commit
7e81600772
@ -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)
|
||||
}
|
||||
|
@ -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()
|
||||
|
Loading…
Reference in New Issue
Block a user