github.com/wangyougui/gf/v2@v2.6.5/text/gstr/gstr_parse.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 gstr 8 9 import ( 10 "net/url" 11 "strings" 12 13 "github.com/wangyougui/gf/v2/errors/gcode" 14 "github.com/wangyougui/gf/v2/errors/gerror" 15 ) 16 17 // Parse parses the string into map[string]interface{}. 18 // 19 // v1=m&v2=n -> map[v1:m v2:n] 20 // v[a]=m&v[b]=n -> map[v:map[a:m b:n]] 21 // v[a][a]=m&v[a][b]=n -> map[v:map[a:map[a:m b:n]]] 22 // v[]=m&v[]=n -> map[v:[m n]] 23 // v[a][]=m&v[a][]=n -> map[v:map[a:[m n]]] 24 // v[][]=m&v[][]=n -> map[v:[map[]]] // Currently does not support nested slice. 25 // v=m&v[a]=n -> error 26 // a .[[b=c -> map[a___[b:c] 27 func Parse(s string) (result map[string]interface{}, err error) { 28 if s == "" { 29 return nil, nil 30 } 31 result = make(map[string]interface{}) 32 parts := strings.Split(s, "&") 33 for _, part := range parts { 34 pos := strings.Index(part, "=") 35 if pos <= 0 { 36 continue 37 } 38 key, err := url.QueryUnescape(part[:pos]) 39 if err != nil { 40 err = gerror.Wrapf(err, `url.QueryUnescape failed for string "%s"`, part[:pos]) 41 return nil, err 42 } 43 44 for len(key) > 0 && key[0] == ' ' { 45 key = key[1:] 46 } 47 48 if key == "" || key[0] == '[' { 49 continue 50 } 51 value, err := url.QueryUnescape(part[pos+1:]) 52 if err != nil { 53 err = gerror.Wrapf(err, `url.QueryUnescape failed for string "%s"`, part[pos+1:]) 54 return nil, err 55 } 56 // split into multiple keys 57 var keys []string 58 left := 0 59 for i, k := range key { 60 if k == '[' && left == 0 { 61 left = i 62 } else if k == ']' { 63 if left > 0 { 64 if len(keys) == 0 { 65 keys = append(keys, key[:left]) 66 } 67 keys = append(keys, key[left+1:i]) 68 left = 0 69 if i+1 < len(key) && key[i+1] != '[' { 70 break 71 } 72 } 73 } 74 } 75 if len(keys) == 0 { 76 keys = append(keys, key) 77 } 78 // first key 79 first := "" 80 for i, chr := range keys[0] { 81 if chr == ' ' || chr == '.' || chr == '[' { 82 first += "_" 83 } else { 84 first += string(chr) 85 } 86 if chr == '[' { 87 first += keys[0][i+1:] 88 break 89 } 90 } 91 keys[0] = first 92 93 // build nested map 94 if err = build(result, keys, value); err != nil { 95 return nil, err 96 } 97 } 98 return result, nil 99 } 100 101 // build nested map. 102 func build(result map[string]interface{}, keys []string, value interface{}) error { 103 var ( 104 length = len(keys) 105 key = strings.Trim(keys[0], "'\"") 106 ) 107 if length == 1 { 108 result[key] = value 109 return nil 110 } 111 112 // The end is slice. like f[], f[a][] 113 if keys[1] == "" && length == 2 { 114 // TODO nested slice 115 if key == "" { 116 return nil 117 } 118 val, ok := result[key] 119 if !ok { 120 result[key] = []interface{}{value} 121 return nil 122 } 123 children, ok := val.([]interface{}) 124 if !ok { 125 return gerror.NewCodef( 126 gcode.CodeInvalidParameter, 127 "expected type '[]interface{}' for key '%s', but got '%T'", 128 key, val, 129 ) 130 } 131 result[key] = append(children, value) 132 return nil 133 } 134 // The end is slice + map. like v[][a] 135 if keys[1] == "" && length > 2 && keys[2] != "" { 136 val, ok := result[key] 137 if !ok { 138 result[key] = []interface{}{} 139 val = result[key] 140 } 141 children, ok := val.([]interface{}) 142 if !ok { 143 return gerror.NewCodef( 144 gcode.CodeInvalidParameter, 145 "expected type '[]interface{}' for key '%s', but got '%T'", 146 key, val, 147 ) 148 } 149 if l := len(children); l > 0 { 150 if child, ok := children[l-1].(map[string]interface{}); ok { 151 if _, ok := child[keys[2]]; !ok { 152 _ = build(child, keys[2:], value) 153 return nil 154 } 155 } 156 } 157 child := map[string]interface{}{} 158 _ = build(child, keys[2:], value) 159 result[key] = append(children, child) 160 return nil 161 } 162 163 // map, like v[a], v[a][b] 164 val, ok := result[key] 165 if !ok { 166 result[key] = map[string]interface{}{} 167 val = result[key] 168 } 169 children, ok := val.(map[string]interface{}) 170 if !ok { 171 return gerror.NewCodef( 172 gcode.CodeInvalidParameter, 173 "expected type 'map[string]interface{}' for key '%s', but got '%T'", 174 key, val, 175 ) 176 } 177 if err := build(children, keys[1:], value); err != nil { 178 return err 179 } 180 return nil 181 }