github.com/zhongdalu/gf@v1.0.0/g/encoding/gjson/gjson.go (about)

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