github.com/gogf/gf@v1.16.9/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/gogf/gf.
     6  
     7  // Package gjson provides convenient API for JSON/XML/INI/YAML/TOML data handling.
     8  package gjson
     9  
    10  import (
    11  	"github.com/gogf/gf/internal/utils"
    12  	"reflect"
    13  	"strconv"
    14  	"strings"
    15  
    16  	"github.com/gogf/gf/internal/rwmutex"
    17  	"github.com/gogf/gf/text/gstr"
    18  	"github.com/gogf/gf/util/gconv"
    19  )
    20  
    21  const (
    22  	// Separator char for hierarchical data access.
    23  	defaultSplitChar = '.'
    24  )
    25  
    26  // Json is the customized JSON struct.
    27  type Json struct {
    28  	mu *rwmutex.RWMutex
    29  	p  *interface{} // Pointer for hierarchical data access, it's the root of data in default.
    30  	c  byte         // Char separator('.' in default).
    31  	vc bool         // Violence Check(false in default), which is used to access data when the hierarchical data key contains separator char.
    32  }
    33  
    34  // Options for Json object creating.
    35  type Options struct {
    36  	Safe      bool   // Mark this object is for in concurrent-safe usage.
    37  	Tags      string // Custom priority tags for decoding.
    38  	StrNumber bool   // StrNumber causes the Decoder to unmarshal a number into an interface{} as a string instead of as a float64.
    39  }
    40  
    41  // apiInterface is used for type assert api for Interface().
    42  type apiInterface interface {
    43  	Interface() interface{}
    44  }
    45  
    46  // setValue sets <value> to <j> by <pattern>.
    47  // Note:
    48  // 1. If value is nil and removed is true, means deleting this value;
    49  // 2. It's quite complicated in hierarchical data search, node creating and data assignment;
    50  func (j *Json) setValue(pattern string, value interface{}, removed bool) error {
    51  	if value != nil {
    52  		if utils.IsStruct(value) {
    53  			if v, ok := value.(apiInterface); ok {
    54  				value = v.Interface()
    55  			}
    56  		}
    57  	}
    58  	array := strings.Split(pattern, string(j.c))
    59  	length := len(array)
    60  	value = j.convertValue(value)
    61  	// Initialization checks.
    62  	if *j.p == nil {
    63  		if gstr.IsNumeric(array[0]) {
    64  			*j.p = make([]interface{}, 0)
    65  		} else {
    66  			*j.p = make(map[string]interface{})
    67  		}
    68  	}
    69  	var pparent *interface{} = nil // Parent pointer.
    70  	var pointer *interface{} = j.p // Current pointer.
    71  	j.mu.Lock()
    72  	defer j.mu.Unlock()
    73  	for i := 0; i < length; i++ {
    74  		switch (*pointer).(type) {
    75  		case map[string]interface{}:
    76  			if i == length-1 {
    77  				if removed && value == nil {
    78  					// Delete item from map.
    79  					delete((*pointer).(map[string]interface{}), array[i])
    80  				} else {
    81  					(*pointer).(map[string]interface{})[array[i]] = value
    82  				}
    83  			} else {
    84  				// If the key does not exit in the map.
    85  				if v, ok := (*pointer).(map[string]interface{})[array[i]]; !ok {
    86  					if removed && value == nil {
    87  						goto done
    88  					}
    89  					// Creating new node.
    90  					if gstr.IsNumeric(array[i+1]) {
    91  						// Creating array node.
    92  						n, _ := strconv.Atoi(array[i+1])
    93  						var v interface{} = make([]interface{}, n+1)
    94  						pparent = j.setPointerWithValue(pointer, array[i], v)
    95  						pointer = &v
    96  					} else {
    97  						// Creating map node.
    98  						var v interface{} = make(map[string]interface{})
    99  						pparent = j.setPointerWithValue(pointer, array[i], v)
   100  						pointer = &v
   101  					}
   102  				} else {
   103  					pparent = pointer
   104  					pointer = &v
   105  				}
   106  			}
   107  
   108  		case []interface{}:
   109  			// A string key.
   110  			if !gstr.IsNumeric(array[i]) {
   111  				if i == length-1 {
   112  					*pointer = map[string]interface{}{array[i]: value}
   113  				} else {
   114  					var v interface{} = make(map[string]interface{})
   115  					*pointer = v
   116  					pparent = pointer
   117  					pointer = &v
   118  				}
   119  				continue
   120  			}
   121  			// Numeric index.
   122  			valueNum, err := strconv.Atoi(array[i])
   123  			if err != nil {
   124  				return err
   125  			}
   126  
   127  			if i == length-1 {
   128  				// Leaf node.
   129  				if len((*pointer).([]interface{})) > valueNum {
   130  					if removed && value == nil {
   131  						// Deleting element.
   132  						if pparent == nil {
   133  							*pointer = append((*pointer).([]interface{})[:valueNum], (*pointer).([]interface{})[valueNum+1:]...)
   134  						} else {
   135  							j.setPointerWithValue(pparent, array[i-1], append((*pointer).([]interface{})[:valueNum], (*pointer).([]interface{})[valueNum+1:]...))
   136  						}
   137  					} else {
   138  						(*pointer).([]interface{})[valueNum] = value
   139  					}
   140  				} else {
   141  					if removed && value == nil {
   142  						goto done
   143  					}
   144  					if pparent == nil {
   145  						// It is the root node.
   146  						j.setPointerWithValue(pointer, array[i], value)
   147  					} else {
   148  						// It is not the root node.
   149  						s := make([]interface{}, valueNum+1)
   150  						copy(s, (*pointer).([]interface{}))
   151  						s[valueNum] = value
   152  						j.setPointerWithValue(pparent, array[i-1], s)
   153  					}
   154  				}
   155  			} else {
   156  				// Branch node.
   157  				if gstr.IsNumeric(array[i+1]) {
   158  					n, _ := strconv.Atoi(array[i+1])
   159  					pSlice := (*pointer).([]interface{})
   160  					if len(pSlice) > valueNum {
   161  						item := pSlice[valueNum]
   162  						if s, ok := item.([]interface{}); ok {
   163  							for i := 0; i < n-len(s); i++ {
   164  								s = append(s, nil)
   165  							}
   166  							pparent = pointer
   167  							pointer = &pSlice[valueNum]
   168  						} else {
   169  							if removed && value == nil {
   170  								goto done
   171  							}
   172  							var v interface{} = make([]interface{}, n+1)
   173  							pparent = j.setPointerWithValue(pointer, array[i], v)
   174  							pointer = &v
   175  						}
   176  					} else {
   177  						if removed && value == nil {
   178  							goto done
   179  						}
   180  						var v interface{} = make([]interface{}, n+1)
   181  						pparent = j.setPointerWithValue(pointer, array[i], v)
   182  						pointer = &v
   183  					}
   184  				} else {
   185  					pSlice := (*pointer).([]interface{})
   186  					if len(pSlice) > valueNum {
   187  						pparent = pointer
   188  						pointer = &(*pointer).([]interface{})[valueNum]
   189  					} else {
   190  						s := make([]interface{}, valueNum+1)
   191  						copy(s, pSlice)
   192  						s[valueNum] = make(map[string]interface{})
   193  						if pparent != nil {
   194  							// i > 0
   195  							j.setPointerWithValue(pparent, array[i-1], s)
   196  							pparent = pointer
   197  							pointer = &s[valueNum]
   198  						} else {
   199  							// i = 0
   200  							var v interface{} = s
   201  							*pointer = v
   202  							pparent = pointer
   203  							pointer = &s[valueNum]
   204  						}
   205  					}
   206  				}
   207  			}
   208  
   209  		// If the variable pointed to by the <pointer> is not of a reference type,
   210  		// then it modifies the variable via its the parent, ie: pparent.
   211  		default:
   212  			if removed && value == nil {
   213  				goto done
   214  			}
   215  			if gstr.IsNumeric(array[i]) {
   216  				n, _ := strconv.Atoi(array[i])
   217  				s := make([]interface{}, n+1)
   218  				if i == length-1 {
   219  					s[n] = value
   220  				}
   221  				if pparent != nil {
   222  					pparent = j.setPointerWithValue(pparent, array[i-1], s)
   223  				} else {
   224  					*pointer = s
   225  					pparent = pointer
   226  				}
   227  			} else {
   228  				var v1, v2 interface{}
   229  				if i == length-1 {
   230  					v1 = map[string]interface{}{
   231  						array[i]: value,
   232  					}
   233  				} else {
   234  					v1 = map[string]interface{}{
   235  						array[i]: nil,
   236  					}
   237  				}
   238  				if pparent != nil {
   239  					pparent = j.setPointerWithValue(pparent, array[i-1], v1)
   240  				} else {
   241  					*pointer = v1
   242  					pparent = pointer
   243  				}
   244  				v2 = v1.(map[string]interface{})[array[i]]
   245  				pointer = &v2
   246  			}
   247  		}
   248  	}
   249  done:
   250  	return nil
   251  }
   252  
   253  // convertValue converts <value> to map[string]interface{} or []interface{},
   254  // which can be supported for hierarchical data access.
   255  func (j *Json) convertValue(value interface{}) interface{} {
   256  	switch value.(type) {
   257  	case map[string]interface{}:
   258  		return value
   259  	case []interface{}:
   260  		return value
   261  	default:
   262  		rv := reflect.ValueOf(value)
   263  		kind := rv.Kind()
   264  		if kind == reflect.Ptr {
   265  			rv = rv.Elem()
   266  			kind = rv.Kind()
   267  		}
   268  		switch kind {
   269  		case reflect.Array:
   270  			return gconv.Interfaces(value)
   271  		case reflect.Slice:
   272  			return gconv.Interfaces(value)
   273  		case reflect.Map:
   274  			return gconv.Map(value)
   275  		case reflect.Struct:
   276  			return gconv.Map(value)
   277  		default:
   278  			// Use json decode/encode at last.
   279  			b, _ := Encode(value)
   280  			v, _ := Decode(b)
   281  			return v
   282  		}
   283  	}
   284  }
   285  
   286  // setPointerWithValue sets <key>:<value> to <pointer>, the <key> may be a map key or slice index.
   287  // It returns the pointer to the new value set.
   288  func (j *Json) setPointerWithValue(pointer *interface{}, key string, value interface{}) *interface{} {
   289  	switch (*pointer).(type) {
   290  	case map[string]interface{}:
   291  		(*pointer).(map[string]interface{})[key] = value
   292  		return &value
   293  	case []interface{}:
   294  		n, _ := strconv.Atoi(key)
   295  		if len((*pointer).([]interface{})) > n {
   296  			(*pointer).([]interface{})[n] = value
   297  			return &(*pointer).([]interface{})[n]
   298  		} else {
   299  			s := make([]interface{}, n+1)
   300  			copy(s, (*pointer).([]interface{}))
   301  			s[n] = value
   302  			*pointer = s
   303  			return &s[n]
   304  		}
   305  	default:
   306  		*pointer = value
   307  	}
   308  	return pointer
   309  }
   310  
   311  // getPointerByPattern returns a pointer to the value by specified <pattern>.
   312  func (j *Json) getPointerByPattern(pattern string) *interface{} {
   313  	if j.vc {
   314  		return j.getPointerByPatternWithViolenceCheck(pattern)
   315  	} else {
   316  		return j.getPointerByPatternWithoutViolenceCheck(pattern)
   317  	}
   318  }
   319  
   320  // getPointerByPatternWithViolenceCheck returns a pointer to the value of specified <pattern> with violence check.
   321  func (j *Json) getPointerByPatternWithViolenceCheck(pattern string) *interface{} {
   322  	if !j.vc {
   323  		return j.getPointerByPatternWithoutViolenceCheck(pattern)
   324  	}
   325  	index := len(pattern)
   326  	start := 0
   327  	length := 0
   328  	pointer := j.p
   329  	if index == 0 {
   330  		return pointer
   331  	}
   332  	for {
   333  		if r := j.checkPatternByPointer(pattern[start:index], pointer); r != nil {
   334  			length += index - start
   335  			if start > 0 {
   336  				length += 1
   337  			}
   338  			start = index + 1
   339  			index = len(pattern)
   340  			if length == len(pattern) {
   341  				return r
   342  			} else {
   343  				pointer = r
   344  			}
   345  		} else {
   346  			// Get the position for next separator char.
   347  			index = strings.LastIndexByte(pattern[start:index], j.c)
   348  			if index != -1 && length > 0 {
   349  				index += length + 1
   350  			}
   351  		}
   352  		if start >= index {
   353  			break
   354  		}
   355  	}
   356  	return nil
   357  }
   358  
   359  // getPointerByPatternWithoutViolenceCheck returns a pointer to the value of specified <pattern>, with no violence check.
   360  func (j *Json) getPointerByPatternWithoutViolenceCheck(pattern string) *interface{} {
   361  	if j.vc {
   362  		return j.getPointerByPatternWithViolenceCheck(pattern)
   363  	}
   364  	pointer := j.p
   365  	if len(pattern) == 0 {
   366  		return pointer
   367  	}
   368  	array := strings.Split(pattern, string(j.c))
   369  	for k, v := range array {
   370  		if r := j.checkPatternByPointer(v, pointer); r != nil {
   371  			if k == len(array)-1 {
   372  				return r
   373  			} else {
   374  				pointer = r
   375  			}
   376  		} else {
   377  			break
   378  		}
   379  	}
   380  	return nil
   381  }
   382  
   383  // checkPatternByPointer checks whether there's value by <key> in specified <pointer>.
   384  // It returns a pointer to the value.
   385  func (j *Json) checkPatternByPointer(key string, pointer *interface{}) *interface{} {
   386  	switch (*pointer).(type) {
   387  	case map[string]interface{}:
   388  		if v, ok := (*pointer).(map[string]interface{})[key]; ok {
   389  			return &v
   390  		}
   391  	case []interface{}:
   392  		if gstr.IsNumeric(key) {
   393  			n, err := strconv.Atoi(key)
   394  			if err == nil && len((*pointer).([]interface{})) > n {
   395  				return &(*pointer).([]interface{})[n]
   396  			}
   397  		}
   398  	}
   399  	return nil
   400  }