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 }