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