gf/encoding/gjson/gjson.go

457 lines
12 KiB
Go
Raw Normal View History

2021-01-17 21:46:25 +08:00
// Copyright GoFrame Author(https://goframe.org). All Rights Reserved.
2017-12-29 16:03:30 +08:00
//
// 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.
2017-12-29 16:03:30 +08:00
// Package gjson provides convenient API for JSON/XML/INI/YAML/TOML data handling.
2017-11-23 10:21:28 +08:00
package gjson
import (
2019-05-08 22:04:36 +08:00
"reflect"
"strconv"
"strings"
2021-12-20 00:32:23 +08:00
"github.com/gogf/gf/v2/errors/gcode"
"github.com/gogf/gf/v2/errors/gerror"
2022-03-11 10:24:42 +08:00
"github.com/gogf/gf/v2/internal/reflection"
2021-10-11 21:41:56 +08:00
"github.com/gogf/gf/v2/internal/rwmutex"
2021-11-13 23:23:55 +08:00
"github.com/gogf/gf/v2/internal/utils"
2021-10-11 21:41:56 +08:00
"github.com/gogf/gf/v2/text/gstr"
"github.com/gogf/gf/v2/util/gconv"
2017-11-23 10:21:28 +08:00
)
2018-04-12 14:09:33 +08:00
const (
2019-06-19 09:06:52 +08:00
// Separator char for hierarchical data access.
2021-01-19 14:26:17 +08:00
defaultSplitChar = '.'
2018-04-12 14:09:33 +08:00
)
// Json is the customized JSON struct.
2017-11-23 10:21:28 +08:00
type Json struct {
2019-06-19 09:06:52 +08:00
mu *rwmutex.RWMutex
p *interface{} // Pointer for hierarchical data access, it's the root of data in default.
c byte // Char separator('.' in default).
2019-07-22 21:20:38 +08:00
vc bool // Violence Check(false in default), which is used to access data when the hierarchical data key contains separator char.
}
// Options for Json object creating.
type Options struct {
Safe bool // Mark this object is for in concurrent-safe usage. This is especially for Json object creating.
Tags string // Custom priority tags for decoding. Eg: "json,yaml,MyTag". This is especially for struct parsing into Json object.
2021-01-19 14:26:17 +08:00
StrNumber bool // StrNumber causes the Decoder to unmarshal a number into an interface{} as a string instead of as a float64.
}
2021-12-20 00:32:23 +08:00
// 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{}
2021-07-08 22:44:16 +08:00
}
2022-01-15 22:15:10 +08:00
// iVal is the interface for underlying interface{} retrieving.
type iVal interface {
Val() interface{}
}
// setValue sets `value` to `j` by `pattern`.
2019-07-22 21:20:38 +08:00
// Note:
// 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 {
2021-12-20 00:32:23 +08:00
var (
err error
array = strings.Split(pattern, string(j.c))
length = len(array)
)
if value, err = j.convertValue(value); err != nil {
return err
2021-07-08 22:44:16 +08:00
}
2020-03-21 21:32:02 +08:00
// Initialization checks.
2019-06-19 09:06:52 +08:00
if *j.p == nil {
if gstr.IsNumeric(array[0]) {
*j.p = make([]interface{}, 0)
} else {
*j.p = make(map[string]interface{})
}
}
var (
pparent *interface{} = nil // Parent pointer.
pointer *interface{} = j.p // Current pointer.
)
2019-06-19 09:06:52 +08:00
j.mu.Lock()
defer j.mu.Unlock()
for i := 0; i < length; i++ {
switch (*pointer).(type) {
case map[string]interface{}:
if i == length-1 {
if removed && value == nil {
2019-07-22 21:20:38 +08:00
// Delete item from map.
2019-06-19 09:06:52 +08:00
delete((*pointer).(map[string]interface{}), array[i])
} else {
2022-02-23 16:54:15 +08:00
if (*pointer).(map[string]interface{}) == nil {
*pointer = map[string]interface{}{}
}
2019-06-19 09:06:52 +08:00
(*pointer).(map[string]interface{})[array[i]] = value
}
} else {
2019-07-22 21:20:38 +08:00
// If the key does not exit in the map.
2019-06-19 09:06:52 +08:00
if v, ok := (*pointer).(map[string]interface{})[array[i]]; !ok {
if removed && value == nil {
goto done
}
2019-07-22 21:20:38 +08:00
// Creating new node.
2019-06-19 09:06:52 +08:00
if gstr.IsNumeric(array[i+1]) {
2019-07-22 21:20:38 +08:00
// Creating array node.
2019-06-19 09:06:52 +08:00
n, _ := strconv.Atoi(array[i+1])
var v interface{} = make([]interface{}, n+1)
pparent = j.setPointerWithValue(pointer, array[i], v)
pointer = &v
} else {
2019-07-22 21:20:38 +08:00
// Creating map node.
2019-06-19 09:06:52 +08:00
var v interface{} = make(map[string]interface{})
pparent = j.setPointerWithValue(pointer, array[i], v)
pointer = &v
}
} else {
pparent = pointer
pointer = &v
}
}
2019-06-19 09:06:52 +08:00
case []interface{}:
2020-08-10 23:03:35 +08:00
// A string key.
2019-06-19 09:06:52 +08:00
if !gstr.IsNumeric(array[i]) {
if i == length-1 {
*pointer = map[string]interface{}{array[i]: value}
} else {
var v interface{} = make(map[string]interface{})
*pointer = v
pparent = pointer
pointer = &v
}
continue
}
2020-08-10 23:03:35 +08:00
// Numeric index.
valueNum, err := strconv.Atoi(array[i])
2019-06-19 09:06:52 +08:00
if err != nil {
err = gerror.WrapCodef(gcode.CodeInvalidParameter, err, `strconv.Atoi failed for string "%s"`, array[i])
2019-06-19 09:06:52 +08:00
return err
}
2020-08-10 23:03:35 +08:00
2019-06-19 09:06:52 +08:00
if i == length-1 {
2020-08-10 23:03:35 +08:00
// Leaf node.
if len((*pointer).([]interface{})) > valueNum {
2019-06-19 09:06:52 +08:00
if removed && value == nil {
2019-07-22 21:20:38 +08:00
// Deleting element.
2019-06-19 09:06:52 +08:00
if pparent == nil {
2020-08-10 23:03:35 +08:00
*pointer = append((*pointer).([]interface{})[:valueNum], (*pointer).([]interface{})[valueNum+1:]...)
2019-06-19 09:06:52 +08:00
} else {
2020-08-10 23:03:35 +08:00
j.setPointerWithValue(pparent, array[i-1], append((*pointer).([]interface{})[:valueNum], (*pointer).([]interface{})[valueNum+1:]...))
2019-06-19 09:06:52 +08:00
}
} else {
2020-08-10 23:03:35 +08:00
(*pointer).([]interface{})[valueNum] = value
2019-06-19 09:06:52 +08:00
}
} else {
if removed && value == nil {
goto done
}
if pparent == nil {
2019-07-22 21:20:38 +08:00
// It is the root node.
2019-06-19 09:06:52 +08:00
j.setPointerWithValue(pointer, array[i], value)
} else {
2019-07-22 21:20:38 +08:00
// It is not the root node.
2020-08-10 23:03:35 +08:00
s := make([]interface{}, valueNum+1)
2019-06-19 09:06:52 +08:00
copy(s, (*pointer).([]interface{}))
2020-08-10 23:03:35 +08:00
s[valueNum] = value
2019-06-19 09:06:52 +08:00
j.setPointerWithValue(pparent, array[i-1], s)
}
}
} else {
2020-08-10 23:03:35 +08:00
// Branch node.
2019-06-19 09:06:52 +08:00
if gstr.IsNumeric(array[i+1]) {
n, _ := strconv.Atoi(array[i+1])
2020-08-10 23:03:35 +08:00
pSlice := (*pointer).([]interface{})
if len(pSlice) > valueNum {
item := pSlice[valueNum]
if s, ok := item.([]interface{}); ok {
for i := 0; i < n-len(s); i++ {
s = append(s, nil)
}
pparent = pointer
pointer = &pSlice[valueNum]
} else {
if removed && value == nil {
goto done
}
var v interface{} = make([]interface{}, n+1)
pparent = j.setPointerWithValue(pointer, array[i], v)
pointer = &v
}
2019-06-19 09:06:52 +08:00
} else {
if removed && value == nil {
goto done
}
var v interface{} = make([]interface{}, n+1)
pparent = j.setPointerWithValue(pointer, array[i], v)
pointer = &v
}
} else {
2020-08-10 23:03:35 +08:00
pSlice := (*pointer).([]interface{})
if len(pSlice) > valueNum {
2020-03-21 21:32:02 +08:00
pparent = pointer
2020-08-10 23:03:35 +08:00
pointer = &(*pointer).([]interface{})[valueNum]
2020-03-21 21:32:02 +08:00
} else {
2020-08-10 23:03:35 +08:00
s := make([]interface{}, valueNum+1)
copy(s, pSlice)
s[valueNum] = make(map[string]interface{})
if pparent != nil {
// i > 0
j.setPointerWithValue(pparent, array[i-1], s)
pparent = pointer
pointer = &s[valueNum]
} else {
// i = 0
var v interface{} = s
*pointer = v
pparent = pointer
pointer = &s[valueNum]
}
2020-03-21 21:32:02 +08:00
}
2019-06-19 09:06:52 +08:00
}
}
2018-01-25 17:43:07 +08:00
// If the variable pointed to by the `pointer` is not of a reference type,
2019-07-22 21:20:38 +08:00
// then it modifies the variable via its the parent, ie: pparent.
2019-06-19 09:06:52 +08:00
default:
if removed && value == nil {
goto done
}
if gstr.IsNumeric(array[i]) {
n, _ := strconv.Atoi(array[i])
s := make([]interface{}, n+1)
if i == length-1 {
s[n] = value
}
if pparent != nil {
pparent = j.setPointerWithValue(pparent, array[i-1], s)
} else {
*pointer = s
pparent = pointer
}
} else {
2020-08-10 23:03:35 +08:00
var v1, v2 interface{}
2019-06-19 09:06:52 +08:00
if i == length-1 {
2020-08-10 23:03:35 +08:00
v1 = map[string]interface{}{
2019-06-19 09:06:52 +08:00
array[i]: value,
}
2020-08-10 23:03:35 +08:00
} else {
v1 = map[string]interface{}{
array[i]: nil,
}
2019-06-19 09:06:52 +08:00
}
if pparent != nil {
2020-08-10 23:03:35 +08:00
pparent = j.setPointerWithValue(pparent, array[i-1], v1)
2019-06-19 09:06:52 +08:00
} else {
2020-08-10 23:03:35 +08:00
*pointer = v1
2019-06-19 09:06:52 +08:00
pparent = pointer
}
2020-08-10 23:03:35 +08:00
v2 = v1.(map[string]interface{})[array[i]]
pointer = &v2
2019-06-19 09:06:52 +08:00
}
}
}
2018-01-23 18:23:05 +08:00
done:
2019-06-19 09:06:52 +08:00
return nil
}
// convertValue converts `value` to map[string]interface{} or []interface{},
// which can be supported for hierarchical data access.
2021-12-20 00:32:23 +08:00
func (j *Json) convertValue(value interface{}) (convertedValue interface{}, err error) {
if value == nil {
return
}
2019-06-19 09:06:52 +08:00
switch value.(type) {
case map[string]interface{}:
2021-12-20 00:32:23 +08:00
return value, nil
2019-06-19 09:06:52 +08:00
case []interface{}:
2021-12-20 00:32:23 +08:00
return value, nil
2019-06-19 09:06:52 +08:00
default:
var (
2022-03-11 10:24:42 +08:00
reflectInfo = reflection.OriginValueAndKind(value)
)
switch reflectInfo.OriginKind {
2019-06-19 09:06:52 +08:00
case reflect.Array:
2021-12-20 00:32:23 +08:00
return gconv.Interfaces(value), nil
2019-06-19 09:06:52 +08:00
case reflect.Slice:
2021-12-20 00:32:23 +08:00
return gconv.Interfaces(value), nil
2019-06-19 09:06:52 +08:00
case reflect.Map:
2021-12-20 00:32:23 +08:00
return gconv.Map(value), nil
2019-06-19 09:06:52 +08:00
case reflect.Struct:
2021-12-20 00:32:23 +08:00
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
2019-06-19 09:06:52 +08:00
default:
2021-12-20 00:32:23 +08:00
return value, nil
2019-06-19 09:06:52 +08:00
}
}
}
// setPointerWithValue sets `key`:`value` to `pointer`, the `key` may be a map key or slice index.
// It returns the pointer to the new value set.
func (j *Json) setPointerWithValue(pointer *interface{}, key string, value interface{}) *interface{} {
2019-06-19 09:06:52 +08:00
switch (*pointer).(type) {
case map[string]interface{}:
(*pointer).(map[string]interface{})[key] = value
return &value
case []interface{}:
n, _ := strconv.Atoi(key)
if len((*pointer).([]interface{})) > n {
(*pointer).([]interface{})[n] = value
return &(*pointer).([]interface{})[n]
} else {
s := make([]interface{}, n+1)
copy(s, (*pointer).([]interface{}))
s[n] = value
*pointer = s
return &s[n]
}
default:
*pointer = value
}
return pointer
}
// getPointerByPattern returns a pointer to the value by specified `pattern`.
func (j *Json) getPointerByPattern(pattern string) *interface{} {
2019-06-19 09:06:52 +08:00
if j.vc {
return j.getPointerByPatternWithViolenceCheck(pattern)
} else {
return j.getPointerByPatternWithoutViolenceCheck(pattern)
}
}
// getPointerByPatternWithViolenceCheck returns a pointer to the value of specified `pattern` with violence check.
func (j *Json) getPointerByPatternWithViolenceCheck(pattern string) *interface{} {
2019-06-19 09:06:52 +08:00
if !j.vc {
return j.getPointerByPatternWithoutViolenceCheck(pattern)
}
// It returns nil if pattern is empty.
if pattern == "" {
return nil
}
// It returns all if pattern is ".".
if pattern == "." {
return j.p
}
var (
index = len(pattern)
start = 0
length = 0
pointer = j.p
)
2019-06-19 09:06:52 +08:00
if index == 0 {
return pointer
}
for {
if r := j.checkPatternByPointer(pattern[start:index], pointer); r != nil {
if length += index - start; start > 0 {
2019-06-19 09:06:52 +08:00
length += 1
}
start = index + 1
index = len(pattern)
if length == len(pattern) {
return r
} else {
pointer = r
}
} else {
// Get the position for next separator char.
index = strings.LastIndexByte(pattern[start:index], j.c)
if index != -1 && length > 0 {
index += length + 1
}
}
if start >= index {
break
}
}
return nil
}
// getPointerByPatternWithoutViolenceCheck returns a pointer to the value of specified `pattern`, with no violence check.
func (j *Json) getPointerByPatternWithoutViolenceCheck(pattern string) *interface{} {
2019-06-19 09:06:52 +08:00
if j.vc {
return j.getPointerByPatternWithViolenceCheck(pattern)
}
// It returns nil if pattern is empty.
if pattern == "" {
return nil
}
// It returns all if pattern is ".".
if pattern == "." {
return j.p
}
2019-06-19 09:06:52 +08:00
pointer := j.p
if len(pattern) == 0 {
return pointer
}
array := strings.Split(pattern, string(j.c))
for k, v := range array {
if r := j.checkPatternByPointer(v, pointer); r != nil {
if k == len(array)-1 {
return r
} else {
pointer = r
}
} else {
break
}
}
return nil
2018-04-12 14:09:33 +08:00
}
// checkPatternByPointer checks whether there's value by `key` in specified `pointer`.
// It returns a pointer to the value.
2018-04-12 14:09:33 +08:00
func (j *Json) checkPatternByPointer(key string, pointer *interface{}) *interface{} {
2019-06-19 09:06:52 +08:00
switch (*pointer).(type) {
case map[string]interface{}:
if v, ok := (*pointer).(map[string]interface{})[key]; ok {
return &v
}
case []interface{}:
if gstr.IsNumeric(key) {
n, err := strconv.Atoi(key)
if err == nil && len((*pointer).([]interface{})) > n {
return &(*pointer).([]interface{})[n]
}
}
}
return nil
2017-11-23 10:21:28 +08:00
}