github.com/bytedance/go-tagexpr/v2@v2.9.8/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  	"errors"
    28  	"fmt"
    29  	"reflect"
    30  	"strings"
    31  
    32  	"github.com/andeya/ameda"
    33  	"github.com/andeya/goutil"
    34  	"github.com/bytedance/go-tagexpr/v2/binding"
    35  	"github.com/bytedance/go-tagexpr/v2/binding/gjson/internal/caching"
    36  	"github.com/bytedance/go-tagexpr/v2/binding/gjson/internal/rt"
    37  	gjson "github.com/bytedance/go-tagexpr/v2/binding/tidwall_gjson"
    38  )
    39  
    40  var (
    41  	programCache         = caching.CreateProgramCache()
    42  	unmarshalerInterface = reflect.TypeOf((*json.Unmarshaler)(nil)).Elem()
    43  )
    44  
    45  func init() {
    46  	gjson.DisableModifiers = true
    47  }
    48  
    49  // UseJSONUnmarshaler reset the JSON Unmarshaler of binding.
    50  func UseJSONUnmarshaler() {
    51  	binding.Default().ResetJSONUnmarshaler(Unmarshal)
    52  }
    53  
    54  // Unmarshal Unmarshal JSON, old version compatible.
    55  func Unmarshal(data []byte, v interface{}) error {
    56  	val, ok := v.(reflect.Value)
    57  	if !ok {
    58  		val = reflect.ValueOf(v)
    59  	}
    60  	return assign(gjson.Parse(ameda.UnsafeBytesToString(data)), val)
    61  }
    62  
    63  // assign Unmarshal
    64  func assign(jsval gjson.Result, goval reflect.Value) (err error) {
    65  	if jsval.Type == gjson.Null {
    66  		return nil
    67  	}
    68  	t := goval.Type()
    69  	switch goval.Kind() {
    70  	default:
    71  	case reflect.Ptr:
    72  		if !ameda.InitPointer(goval) {
    73  			return errors.New("v cannot be set")
    74  		}
    75  		newval := ameda.DereferencePtrValue(goval)
    76  		if err = assign(jsval, newval); err != nil {
    77  			return err
    78  		}
    79  	case reflect.Struct:
    80  		sf := getFiledInfo(t)
    81  		jsval.ForEach(func(key, value gjson.Result) bool {
    82  			if idx, ok := sf[key.Str]; ok {
    83  				f := fieldByIndex(goval, idx)
    84  				if f.CanSet() {
    85  					if err = assign(value, f); err != nil {
    86  						return false
    87  					}
    88  				}
    89  			}
    90  			return true
    91  		})
    92  	case reflect.Slice:
    93  		if t.Elem().Kind() == reflect.Uint8 && jsval.Type == gjson.String {
    94  			var data []byte
    95  			data, err = base64.StdEncoding.DecodeString(jsval.String())
    96  			if err != nil {
    97  				return err
    98  			}
    99  			goval.Set(reflect.ValueOf(data))
   100  		} else {
   101  			if !jsval.IsArray() {
   102  				// canAddr: true, implement unmarshaler : true   -> continue
   103  				// canAddr: true, implement unmarshaler : false  -> return
   104  				// canAddr: false, implement unmarshaler : true  -> return
   105  				// canAddr: false, implement unmarshaler : false -> return
   106  				if !goval.CanAddr() || !goval.Addr().Type().Implements(unmarshalerInterface) {
   107  					return nil
   108  				}
   109  			}
   110  			jsvals := jsval.Array()
   111  			slice := reflect.MakeSlice(t, len(jsvals), len(jsvals))
   112  			for i := 0; i < len(jsvals); i++ {
   113  				if err = assign(jsvals[i], slice.Index(i)); err != nil {
   114  					return err
   115  				}
   116  			}
   117  			goval.Set(slice)
   118  		}
   119  	case reflect.Array:
   120  		i, n := 0, goval.Len()
   121  		jsval.ForEach(func(_, value gjson.Result) bool {
   122  			if i == n {
   123  				return false
   124  			}
   125  			if err = assign(value, goval.Index(i)); err != nil {
   126  				return false
   127  			}
   128  			i++
   129  			return true
   130  		})
   131  	case reflect.Map:
   132  		if jsval.Type == gjson.JSON && t.Key().Kind() == reflect.String {
   133  			if t.Elem().Kind() == reflect.Interface {
   134  				goval.Set(reflect.ValueOf(jsval.Value()))
   135  			} else {
   136  				if goval.IsNil() {
   137  					goval.Set(reflect.MakeMap(t))
   138  				}
   139  				valType := t.Elem()
   140  				keyType := goval.Type().Key()
   141  				switch keyType.Kind() {
   142  				case reflect.String, reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64,
   143  					reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
   144  				default:
   145  					return fmt.Errorf("gjson: unsupported type: %s", keyType)
   146  				}
   147  				jsval.ForEach(func(key, value gjson.Result) bool {
   148  					val := reflect.New(valType)
   149  					if err = assign(value, val); err != nil {
   150  						return false
   151  					}
   152  					goval.SetMapIndex(reflect.ValueOf(key.String()).Convert(keyType), val.Elem())
   153  					return true
   154  				})
   155  			}
   156  		}
   157  	case reflect.Interface:
   158  		goval.Set(reflect.ValueOf(jsval.Value()))
   159  	case reflect.Bool:
   160  		goval.SetBool(jsval.Bool())
   161  	case reflect.Float32, reflect.Float64:
   162  		goval.SetFloat(jsval.Float())
   163  	case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
   164  		goval.SetInt(jsval.Int())
   165  	case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
   166  		goval.SetUint(jsval.Uint())
   167  	case reflect.String:
   168  		goval.SetString(jsval.String())
   169  	}
   170  	if len(t.PkgPath()) > 0 {
   171  		v := goval.Addr()
   172  		if v.Type().NumMethod() > 0 {
   173  			if u, ok := v.Interface().(json.Unmarshaler); ok {
   174  				if err = u.UnmarshalJSON([]byte(jsval.Raw)); err != nil {
   175  					return err
   176  				}
   177  			}
   178  		}
   179  	}
   180  	return err
   181  }
   182  
   183  func getFiledInfo(t reflect.Type) map[string][]int {
   184  	vtr := rt.UnpackType(t)
   185  	filedInfo := programCache.Get(vtr)
   186  	if filedInfo == nil {
   187  		pp, err := programCache.Compute(vtr, computeTypeInfo)
   188  		if err == nil {
   189  			return pp.(map[string][]int)
   190  		}
   191  		filedInfo, _ = computeTypeInfo(vtr)
   192  	}
   193  	return filedInfo.(map[string][]int)
   194  }
   195  
   196  func computeTypeInfo(vtr *rt.GoType) (interface{}, error) {
   197  	t := vtr.Pack()
   198  	sf := make(map[string][]int)
   199  	numField := t.NumField()
   200  	for i := 0; i < numField; i++ {
   201  		f := t.Field(i)
   202  		if !f.Anonymous && !goutil.IsExportedName(f.Name) {
   203  			continue
   204  		}
   205  		tag := getJsonTag(f.Tag)
   206  		if tag == "-" {
   207  			continue
   208  		}
   209  		if tag != "" {
   210  			sf[tag] = []int{i}
   211  		} else if f.Anonymous {
   212  			if findAnonymous(ameda.DereferenceType(f.Type), []int{i}, sf, 20) {
   213  				continue
   214  			}
   215  		}
   216  		if tag != f.Name {
   217  			sf[f.Name] = []int{i}
   218  		}
   219  	}
   220  	return sf, nil
   221  }
   222  
   223  func getJsonTag(tag reflect.StructTag) string {
   224  	return strings.Split(tag.Get("json"), ",")[0]
   225  }
   226  
   227  func findAnonymous(t reflect.Type, i []int, sf map[string][]int, depth int) bool {
   228  	depth--
   229  	if depth < 0 {
   230  		return true
   231  	}
   232  	if t.Kind() == reflect.Struct {
   233  		subNumField := t.NumField()
   234  		for ii := 0; ii < subNumField; ii++ {
   235  			ff := t.Field(ii)
   236  			subTag := getJsonTag(ff.Tag)
   237  			if subTag == "-" {
   238  				continue
   239  			}
   240  			a := append(i, ii)
   241  			if subTag != "" {
   242  				sf[subTag] = a
   243  			} else if ff.Anonymous {
   244  				tt := ameda.DereferenceType(ff.Type)
   245  				if tt.String() == t.String() {
   246  					continue
   247  				}
   248  				if findAnonymous(tt, a, sf, depth) {
   249  					continue
   250  				}
   251  			}
   252  			if subTag != ff.Name {
   253  				sf[ff.Name] = a
   254  			}
   255  		}
   256  		return true
   257  	}
   258  	return false
   259  }
   260  
   261  func fieldByIndex(v reflect.Value, index []int) reflect.Value {
   262  	if len(index) == 1 {
   263  		return v.Field(index[0])
   264  	}
   265  	if v.Kind() != reflect.Struct {
   266  		return reflect.Value{}
   267  	}
   268  	v = v.Field(index[0])
   269  	for _, x := range index[1:] {
   270  		for v.Kind() == reflect.Ptr && v.Type().Elem().Kind() == reflect.Struct {
   271  			if v.IsNil() {
   272  				if v.CanSet() {
   273  					ptrDepth := 0
   274  					t := v.Type()
   275  					for t.Kind() == reflect.Ptr {
   276  						t = t.Elem()
   277  						ptrDepth++
   278  					}
   279  					v.Set(ameda.ReferenceValue(reflect.New(t), ptrDepth-1))
   280  					v = ameda.DereferencePtrValue(v)
   281  				} else {
   282  					return reflect.Value{}
   283  				}
   284  			} else {
   285  				v = ameda.DereferencePtrValue(v)
   286  			}
   287  		}
   288  		v = v.Field(x)
   289  	}
   290  	return v
   291  }