github.com/bytedance/go-tagexpr@v2.7.5-0.20210114074101-de5b8743ad85+incompatible/binding/gjson/gjson.go (about)

     1  // The MIT License (MIT)
     2  
     3  // Copyright (c) 2016 Josh Baker
     4  
     5  // Permission is hereby granted, free of charge, to any person obtaining a copy of
     6  // this software and associated documentation files (the "Software"), to deal in
     7  // the Software without restriction, including without limitation the rights to
     8  // use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
     9  // the Software, and to permit persons to whom the Software is furnished to do so,
    10  // subject to the following conditions:
    11  
    12  // The above copyright notice and this permission notice shall be included in all
    13  // copies or substantial portions of the Software.
    14  
    15  // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
    16  // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
    17  // FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
    18  // COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
    19  // IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
    20  // CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
    21  
    22  package gjson
    23  
    24  import (
    25  	"encoding/base64"
    26  	"encoding/json"
    27  	"reflect"
    28  	"strings"
    29  	"sync"
    30  
    31  	"github.com/henrylee2cn/ameda"
    32  	"github.com/henrylee2cn/goutil/tpack"
    33  	"github.com/tidwall/gjson"
    34  
    35  	"github.com/bytedance/go-tagexpr/binding"
    36  )
    37  
    38  var fieldsmu sync.RWMutex
    39  var fields = make(map[uintptr]map[string]int)
    40  
    41  func init() {
    42  	gjson.DisableModifiers = true
    43  }
    44  
    45  // UseJSONUnmarshaler reset the JSON Unmarshaler of binding.
    46  func UseJSONUnmarshaler() {
    47  	binding.ResetJSONUnmarshaler(unmarshal)
    48  }
    49  
    50  // unmarshal unmarshal JSON, old version compatible.
    51  func unmarshal(data []byte, v interface{}) error {
    52  	val, ok := v.(reflect.Value)
    53  	if !ok {
    54  		val = reflect.ValueOf(v)
    55  	}
    56  	assign(gjson.Parse(ameda.UnsafeBytesToString(data)), val)
    57  	return nil
    58  }
    59  
    60  // assign unmarshal
    61  func assign(jsval gjson.Result, goval reflect.Value) {
    62  	if jsval.Type == gjson.Null {
    63  		return
    64  	}
    65  	t := goval.Type()
    66  	switch goval.Kind() {
    67  	default:
    68  	case reflect.Ptr:
    69  		if !goval.IsNil() {
    70  			newval := reflect.New(goval.Elem().Type())
    71  			assign(jsval, newval.Elem())
    72  			goval.Elem().Set(newval.Elem())
    73  		} else {
    74  			newval := reflect.New(t.Elem())
    75  			assign(jsval, newval.Elem())
    76  			goval.Set(newval)
    77  		}
    78  	case reflect.Struct:
    79  		runtimeTypeID := tpack.From(goval).RuntimeTypeID()
    80  		fieldsmu.RLock()
    81  		sf := fields[runtimeTypeID]
    82  		fieldsmu.RUnlock()
    83  		if sf == nil {
    84  			fieldsmu.Lock()
    85  			sf = make(map[string]int)
    86  			numField := t.NumField()
    87  			for i := 0; i < numField; i++ {
    88  				f := t.Field(i)
    89  				tag := strings.Split(f.Tag.Get("json"), ",")[0]
    90  				if tag != "-" {
    91  					if tag != "" {
    92  						sf[tag] = i
    93  						sf[f.Name] = i
    94  					} else {
    95  						sf[f.Name] = i
    96  					}
    97  				}
    98  			}
    99  			fields[runtimeTypeID] = sf
   100  			fieldsmu.Unlock()
   101  		}
   102  		jsval.ForEach(func(key, value gjson.Result) bool {
   103  			if idx, ok := sf[key.Str]; ok {
   104  				f := goval.Field(idx)
   105  				if f.CanSet() {
   106  					assign(value, f)
   107  				}
   108  			}
   109  			return true
   110  		})
   111  	case reflect.Slice:
   112  		if t.Elem().Kind() == reflect.Uint8 && jsval.Type == gjson.String {
   113  			data, _ := base64.StdEncoding.DecodeString(jsval.String())
   114  			goval.Set(reflect.ValueOf(data))
   115  		} else {
   116  			jsvals := jsval.Array()
   117  			slice := reflect.MakeSlice(t, len(jsvals), len(jsvals))
   118  			for i := 0; i < len(jsvals); i++ {
   119  				assign(jsvals[i], slice.Index(i))
   120  			}
   121  			goval.Set(slice)
   122  		}
   123  	case reflect.Array:
   124  		i, n := 0, goval.Len()
   125  		jsval.ForEach(func(_, value gjson.Result) bool {
   126  			if i == n {
   127  				return false
   128  			}
   129  			assign(value, goval.Index(i))
   130  			i++
   131  			return true
   132  		})
   133  	case reflect.Map:
   134  		if jsval.Type == gjson.JSON && t.Key().Kind() == reflect.String {
   135  			if t.Elem().Kind() == reflect.Interface {
   136  				goval.Set(reflect.ValueOf(jsval.Value()))
   137  			} else {
   138  				if goval.IsNil() {
   139  					goval.Set(reflect.MakeMap(t))
   140  				}
   141  				valType := t.Elem()
   142  				jsval.ForEach(func(key, value gjson.Result) bool {
   143  					val := reflect.New(valType)
   144  					assign(value, val)
   145  					goval.SetMapIndex(reflect.ValueOf(key.String()), val.Elem())
   146  					return true
   147  				})
   148  			}
   149  		}
   150  	case reflect.Interface:
   151  		goval.Set(reflect.ValueOf(jsval.Value()))
   152  	case reflect.Bool:
   153  		goval.SetBool(jsval.Bool())
   154  	case reflect.Float32, reflect.Float64:
   155  		goval.SetFloat(jsval.Float())
   156  	case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
   157  		goval.SetInt(jsval.Int())
   158  	case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
   159  		goval.SetUint(jsval.Uint())
   160  	case reflect.String:
   161  		goval.SetString(jsval.String())
   162  	}
   163  	if len(t.PkgPath()) > 0 {
   164  		v := goval.Addr()
   165  		if v.Type().NumMethod() > 0 {
   166  			if u, ok := v.Interface().(json.Unmarshaler); ok {
   167  				u.UnmarshalJSON([]byte(jsval.Raw))
   168  			}
   169  		}
   170  	}
   171  }