github.com/wangyougui/gf/v2@v2.6.5/encoding/gjson/gjson.go (about)

     1  // Copyright GoFrame Author(https://goframe.org). All Rights Reserved.
     2  //
     3  // This Source Code Form is subject to the terms of the MIT License.
     4  // If a copy of the MIT was not distributed with this file,
     5  // You can obtain one at https://github.com/wangyougui/gf.
     6  
     7  // Package gjson provides convenient API for JSON/XML/INI/YAML/TOML data handling.
     8  package gjson
     9  
    10  import (
    11  	"reflect"
    12  	"strconv"
    13  	"strings"
    14  
    15  	"github.com/wangyougui/gf/v2/errors/gcode"
    16  	"github.com/wangyougui/gf/v2/errors/gerror"
    17  	"github.com/wangyougui/gf/v2/internal/reflection"
    18  	"github.com/wangyougui/gf/v2/internal/rwmutex"
    19  	"github.com/wangyougui/gf/v2/internal/utils"
    20  	"github.com/wangyougui/gf/v2/text/gstr"
    21  	"github.com/wangyougui/gf/v2/util/gconv"
    22  )
    23  
    24  type ContentType string
    25  
    26  const (
    27  	ContentTypeJson       ContentType = `json`
    28  	ContentTypeJs         ContentType = `js`
    29  	ContentTypeXml        ContentType = `xml`
    30  	ContentTypeIni        ContentType = `ini`
    31  	ContentTypeYaml       ContentType = `yaml`
    32  	ContentTypeYml        ContentType = `yml`
    33  	ContentTypeToml       ContentType = `toml`
    34  	ContentTypeProperties ContentType = `properties`
    35  )
    36  
    37  const (
    38  	defaultSplitChar = '.' // Separator char for hierarchical data access.
    39  )
    40  
    41  // Json is the customized JSON struct.
    42  type Json struct {
    43  	mu rwmutex.RWMutex
    44  	p  *interface{} // Pointer for hierarchical data access, it's the root of data in default.
    45  	c  byte         // Char separator('.' in default).
    46  	vc bool         // Violence Check(false in default), which is used to access data when the hierarchical data key contains separator char.
    47  }
    48  
    49  // Options for Json object creating/loading.
    50  type Options struct {
    51  	Safe      bool        // Mark this object is for in concurrent-safe usage. This is especially for Json object creating.
    52  	Tags      string      // Custom priority tags for decoding, eg: "json,yaml,MyTag". This is especially for struct parsing into Json object.
    53  	Type      ContentType // Type specifies the data content type, eg: json, xml, yaml, toml, ini.
    54  	StrNumber bool        // StrNumber causes the Decoder to unmarshal a number into an interface{} as a string instead of as a float64.
    55  }
    56  
    57  // iInterfaces is used for type assert api for Interfaces().
    58  type iInterfaces interface {
    59  	Interfaces() []interface{}
    60  }
    61  
    62  // iMapStrAny is the interface support for converting struct parameter to map.
    63  type iMapStrAny interface {
    64  	MapStrAny() map[string]interface{}
    65  }
    66  
    67  // iVal is the interface for underlying interface{} retrieving.
    68  type iVal interface {
    69  	Val() interface{}
    70  }
    71  
    72  // setValue sets `value` to `j` by `pattern`.
    73  // Note:
    74  // 1. If value is nil and removed is true, means deleting this value;
    75  // 2. It's quite complicated in hierarchical data search, node creating and data assignment;
    76  func (j *Json) setValue(pattern string, value interface{}, removed bool) error {
    77  	var (
    78  		err    error
    79  		array  = strings.Split(pattern, string(j.c))
    80  		length = len(array)
    81  	)
    82  	if value, err = j.convertValue(value); err != nil {
    83  		return err
    84  	}
    85  	// Initialization checks.
    86  	if *j.p == nil {
    87  		if gstr.IsNumeric(array[0]) {
    88  			*j.p = make([]interface{}, 0)
    89  		} else {
    90  			*j.p = make(map[string]interface{})
    91  		}
    92  	}
    93  	var (
    94  		pparent *interface{} = nil // Parent pointer.
    95  		pointer *interface{} = j.p // Current pointer.
    96  	)
    97  	j.mu.Lock()
    98  	defer j.mu.Unlock()
    99  	for i := 0; i < length; i++ {
   100  		switch (*pointer).(type) {
   101  		case map[string]interface{}:
   102  			if i == length-1 {
   103  				if removed && value == nil {
   104  					// Delete item from map.
   105  					delete((*pointer).(map[string]interface{}), array[i])
   106  				} else {
   107  					if (*pointer).(map[string]interface{}) == nil {
   108  						*pointer = map[string]interface{}{}
   109  					}
   110  					(*pointer).(map[string]interface{})[array[i]] = value
   111  				}
   112  			} else {
   113  				// If the key does not exit in the map.
   114  				if v, ok := (*pointer).(map[string]interface{})[array[i]]; !ok {
   115  					if removed && value == nil {
   116  						goto done
   117  					}
   118  					// Creating new node.
   119  					if gstr.IsNumeric(array[i+1]) {
   120  						// Creating array node.
   121  						n, _ := strconv.Atoi(array[i+1])
   122  						var v interface{} = make([]interface{}, n+1)
   123  						pparent = j.setPointerWithValue(pointer, array[i], v)
   124  						pointer = &v
   125  					} else {
   126  						// Creating map node.
   127  						var v interface{} = make(map[string]interface{})
   128  						pparent = j.setPointerWithValue(pointer, array[i], v)
   129  						pointer = &v
   130  					}
   131  				} else {
   132  					pparent = pointer
   133  					pointer = &v
   134  				}
   135  			}
   136  
   137  		case []interface{}:
   138  			// A string key.
   139  			if !gstr.IsNumeric(array[i]) {
   140  				if i == length-1 {
   141  					*pointer = map[string]interface{}{array[i]: value}
   142  				} else {
   143  					var v interface{} = make(map[string]interface{})
   144  					*pointer = v
   145  					pparent = pointer
   146  					pointer = &v
   147  				}
   148  				continue
   149  			}
   150  			// Numeric index.
   151  			valueNum, err := strconv.Atoi(array[i])
   152  			if err != nil {
   153  				err = gerror.WrapCodef(gcode.CodeInvalidParameter, err, `strconv.Atoi failed for string "%s"`, array[i])
   154  				return err
   155  			}
   156  
   157  			if i == length-1 {
   158  				// Leaf node.
   159  				if len((*pointer).([]interface{})) > valueNum {
   160  					if removed && value == nil {
   161  						// Deleting element.
   162  						if pparent == nil {
   163  							*pointer = append((*pointer).([]interface{})[:valueNum], (*pointer).([]interface{})[valueNum+1:]...)
   164  						} else {
   165  							j.setPointerWithValue(pparent, array[i-1], append((*pointer).([]interface{})[:valueNum], (*pointer).([]interface{})[valueNum+1:]...))
   166  						}
   167  					} else {
   168  						(*pointer).([]interface{})[valueNum] = value
   169  					}
   170  				} else {
   171  					if removed && value == nil {
   172  						goto done
   173  					}
   174  					if pparent == nil {
   175  						// It is the root node.
   176  						j.setPointerWithValue(pointer, array[i], value)
   177  					} else {
   178  						// It is not the root node.
   179  						s := make([]interface{}, valueNum+1)
   180  						copy(s, (*pointer).([]interface{}))
   181  						s[valueNum] = value
   182  						j.setPointerWithValue(pparent, array[i-1], s)
   183  					}
   184  				}
   185  			} else {
   186  				// Branch node.
   187  				if gstr.IsNumeric(array[i+1]) {
   188  					n, _ := strconv.Atoi(array[i+1])
   189  					pSlice := (*pointer).([]interface{})
   190  					if len(pSlice) > valueNum {
   191  						item := pSlice[valueNum]
   192  						if s, ok := item.([]interface{}); ok {
   193  							for i := 0; i < n-len(s); i++ {
   194  								s = append(s, nil)
   195  							}
   196  							pparent = pointer
   197  							pointer = &pSlice[valueNum]
   198  						} else {
   199  							if removed && value == nil {
   200  								goto done
   201  							}
   202  							var v interface{} = make([]interface{}, n+1)
   203  							pparent = j.setPointerWithValue(pointer, array[i], v)
   204  							pointer = &v
   205  						}
   206  					} else {
   207  						if removed && value == nil {
   208  							goto done
   209  						}
   210  						var v interface{} = make([]interface{}, n+1)
   211  						pparent = j.setPointerWithValue(pointer, array[i], v)
   212  						pointer = &v
   213  					}
   214  				} else {
   215  					pSlice := (*pointer).([]interface{})
   216  					if len(pSlice) > valueNum {
   217  						pparent = pointer
   218  						pointer = &(*pointer).([]interface{})[valueNum]
   219  					} else {
   220  						s := make([]interface{}, valueNum+1)
   221  						copy(s, pSlice)
   222  						s[valueNum] = make(map[string]interface{})
   223  						if pparent != nil {
   224  							// i > 0
   225  							j.setPointerWithValue(pparent, array[i-1], s)
   226  							pparent = pointer
   227  							pointer = &s[valueNum]
   228  						} else {
   229  							// i = 0
   230  							var v interface{} = s
   231  							*pointer = v
   232  							pparent = pointer
   233  							pointer = &s[valueNum]
   234  						}
   235  					}
   236  				}
   237  			}
   238  
   239  		// If the variable pointed to by the `pointer` is not of a reference type,
   240  		// then it modifies the variable via its the parent, ie: pparent.
   241  		default:
   242  			if removed && value == nil {
   243  				goto done
   244  			}
   245  			if gstr.IsNumeric(array[i]) {
   246  				n, _ := strconv.Atoi(array[i])
   247  				s := make([]interface{}, n+1)
   248  				if i == length-1 {
   249  					s[n] = value
   250  				}
   251  				if pparent != nil {
   252  					pparent = j.setPointerWithValue(pparent, array[i-1], s)
   253  				} else {
   254  					*pointer = s
   255  					pparent = pointer
   256  				}
   257  			} else {
   258  				var v1, v2 interface{}
   259  				if i == length-1 {
   260  					v1 = map[string]interface{}{
   261  						array[i]: value,
   262  					}
   263  				} else {
   264  					v1 = map[string]interface{}{
   265  						array[i]: nil,
   266  					}
   267  				}
   268  				if pparent != nil {
   269  					pparent = j.setPointerWithValue(pparent, array[i-1], v1)
   270  				} else {
   271  					*pointer = v1
   272  					pparent = pointer
   273  				}
   274  				v2 = v1.(map[string]interface{})[array[i]]
   275  				pointer = &v2
   276  			}
   277  		}
   278  	}
   279  done:
   280  	return nil
   281  }
   282  
   283  // convertValue converts `value` to map[string]interface{} or []interface{},
   284  // which can be supported for hierarchical data access.
   285  func (j *Json) convertValue(value interface{}) (convertedValue interface{}, err error) {
   286  	if value == nil {
   287  		return
   288  	}
   289  
   290  	switch value.(type) {
   291  	case map[string]interface{}:
   292  		return value, nil
   293  
   294  	case []interface{}:
   295  		return value, nil
   296  
   297  	default:
   298  		var (
   299  			reflectInfo = reflection.OriginValueAndKind(value)
   300  		)
   301  		switch reflectInfo.OriginKind {
   302  		case reflect.Array:
   303  			return gconv.Interfaces(value), nil
   304  
   305  		case reflect.Slice:
   306  			return gconv.Interfaces(value), nil
   307  
   308  		case reflect.Map:
   309  			return gconv.Map(value), nil
   310  
   311  		case reflect.Struct:
   312  			if v, ok := value.(iMapStrAny); ok {
   313  				convertedValue = v.MapStrAny()
   314  			}
   315  			if utils.IsNil(convertedValue) {
   316  				if v, ok := value.(iInterfaces); ok {
   317  					convertedValue = v.Interfaces()
   318  				}
   319  			}
   320  			if utils.IsNil(convertedValue) {
   321  				convertedValue = gconv.Map(value)
   322  			}
   323  			if utils.IsNil(convertedValue) {
   324  				err = gerror.NewCodef(gcode.CodeInvalidParameter, `unsupported value type "%s"`, reflect.TypeOf(value))
   325  			}
   326  			return
   327  
   328  		default:
   329  			return value, nil
   330  		}
   331  	}
   332  }
   333  
   334  // setPointerWithValue sets `key`:`value` to `pointer`, the `key` may be a map key or slice index.
   335  // It returns the pointer to the new value set.
   336  func (j *Json) setPointerWithValue(pointer *interface{}, key string, value interface{}) *interface{} {
   337  	switch (*pointer).(type) {
   338  	case map[string]interface{}:
   339  		(*pointer).(map[string]interface{})[key] = value
   340  		return &value
   341  	case []interface{}:
   342  		n, _ := strconv.Atoi(key)
   343  		if len((*pointer).([]interface{})) > n {
   344  			(*pointer).([]interface{})[n] = value
   345  			return &(*pointer).([]interface{})[n]
   346  		} else {
   347  			s := make([]interface{}, n+1)
   348  			copy(s, (*pointer).([]interface{}))
   349  			s[n] = value
   350  			*pointer = s
   351  			return &s[n]
   352  		}
   353  	default:
   354  		*pointer = value
   355  	}
   356  	return pointer
   357  }
   358  
   359  // getPointerByPattern returns a pointer to the value by specified `pattern`.
   360  func (j *Json) getPointerByPattern(pattern string) *interface{} {
   361  	if j.p == nil {
   362  		return nil
   363  	}
   364  	if j.vc {
   365  		return j.getPointerByPatternWithViolenceCheck(pattern)
   366  	} else {
   367  		return j.getPointerByPatternWithoutViolenceCheck(pattern)
   368  	}
   369  }
   370  
   371  // getPointerByPatternWithViolenceCheck returns a pointer to the value of specified `pattern` with violence check.
   372  func (j *Json) getPointerByPatternWithViolenceCheck(pattern string) *interface{} {
   373  	if !j.vc {
   374  		return j.getPointerByPatternWithoutViolenceCheck(pattern)
   375  	}
   376  
   377  	// It returns nil if pattern is empty.
   378  	if pattern == "" {
   379  		return nil
   380  	}
   381  	// It returns all if pattern is ".".
   382  	if pattern == "." {
   383  		return j.p
   384  	}
   385  
   386  	var (
   387  		index   = len(pattern)
   388  		start   = 0
   389  		length  = 0
   390  		pointer = j.p
   391  	)
   392  	if index == 0 {
   393  		return pointer
   394  	}
   395  	for {
   396  		if r := j.checkPatternByPointer(pattern[start:index], pointer); r != nil {
   397  			if length += index - start; start > 0 {
   398  				length += 1
   399  			}
   400  			start = index + 1
   401  			index = len(pattern)
   402  			if length == len(pattern) {
   403  				return r
   404  			} else {
   405  				pointer = r
   406  			}
   407  		} else {
   408  			// Get the position for next separator char.
   409  			index = strings.LastIndexByte(pattern[start:index], j.c)
   410  			if index != -1 && length > 0 {
   411  				index += length + 1
   412  			}
   413  		}
   414  		if start >= index {
   415  			break
   416  		}
   417  	}
   418  	return nil
   419  }
   420  
   421  // getPointerByPatternWithoutViolenceCheck returns a pointer to the value of specified `pattern`, with no violence check.
   422  func (j *Json) getPointerByPatternWithoutViolenceCheck(pattern string) *interface{} {
   423  	if j.vc {
   424  		return j.getPointerByPatternWithViolenceCheck(pattern)
   425  	}
   426  
   427  	// It returns nil if pattern is empty.
   428  	if pattern == "" {
   429  		return nil
   430  	}
   431  	// It returns all if pattern is ".".
   432  	if pattern == "." {
   433  		return j.p
   434  	}
   435  
   436  	pointer := j.p
   437  	if len(pattern) == 0 {
   438  		return pointer
   439  	}
   440  	array := strings.Split(pattern, string(j.c))
   441  	for k, v := range array {
   442  		if r := j.checkPatternByPointer(v, pointer); r != nil {
   443  			if k == len(array)-1 {
   444  				return r
   445  			} else {
   446  				pointer = r
   447  			}
   448  		} else {
   449  			break
   450  		}
   451  	}
   452  	return nil
   453  }
   454  
   455  // checkPatternByPointer checks whether there's value by `key` in specified `pointer`.
   456  // It returns a pointer to the value.
   457  func (j *Json) checkPatternByPointer(key string, pointer *interface{}) *interface{} {
   458  	switch (*pointer).(type) {
   459  	case map[string]interface{}:
   460  		if v, ok := (*pointer).(map[string]interface{})[key]; ok {
   461  			return &v
   462  		}
   463  	case []interface{}:
   464  		if gstr.IsNumeric(key) {
   465  			n, err := strconv.Atoi(key)
   466  			if err == nil && len((*pointer).([]interface{})) > n {
   467  				return &(*pointer).([]interface{})[n]
   468  			}
   469  		}
   470  	}
   471  	return nil
   472  }