package bond import ( "strings" "fmt" "net/url" ) func ParseStr(encodedString string, result map[string]interface{}) error { // build nested map. var build func(map[string]interface{}, []string, interface{}) error build = func(result map[string]interface{}, keys []string, value interface{}) error { length := len(keys) // trim '," key := strings.Trim(keys[0], "'\"") if length == 1 { result[key] = value return nil } // The end is slice. like f[], f[a][] if keys[1] == "" && length == 2 { // todo nested slice if key == "" { return nil } val, ok := result[key] if !ok { result[key] = []interface{}{value} return nil } children, ok := val.([]interface{}) if !ok { return fmt.Errorf("expected type '[]interface{}' for key '%s', but got '%T'", key, val) } result[key] = append(children, value) return nil } // The end is slice + map. like f[][a] if keys[1] == "" && length > 2 && keys[2] != "" { val, ok := result[key] if !ok { result[key] = []interface{}{} val = result[key] } children, ok := val.([]interface{}) if !ok { return fmt.Errorf("expected type '[]interface{}' for key '%s', but got '%T'", key, val) } if l := len(children); l > 0 { if child, ok := children[l-1].(map[string]interface{}); ok { if _, ok := child[keys[2]]; !ok { _ = build(child, keys[2:], value) return nil } } } child := map[string]interface{}{} _ = build(child, keys[2:], value) result[key] = append(children, child) return nil } // map. like f[a], f[a][b] val, ok := result[key] if !ok { result[key] = map[string]interface{}{} val = result[key] } children, ok := val.(map[string]interface{}) if !ok { return fmt.Errorf("expected type 'map[string]interface{}' for key '%s', but got '%T'", key, val) } return build(children, keys[1:], value) } // split encodedString. parts := strings.Split(encodedString, "&") for _, part := range parts { pos := strings.Index(part, "=") if pos <= 0 { continue } key, err := url.QueryUnescape(part[:pos]) if err != nil { return err } for key[0] == ' ' { key = key[1:] } if key == "" || key[0] == '[' { continue } value, err := url.QueryUnescape(part[pos+1:]) if err != nil { return err } // split into multiple keys var keys []string left := 0 for i, k := range key { if k == '[' && left == 0 { left = i } else if k == ']' { if left > 0 { if len(keys) == 0 { keys = append(keys, key[:left]) } keys = append(keys, key[left+1:i]) left = 0 if i+1 < len(key) && key[i+1] != '[' { break } } } } if len(keys) == 0 { keys = append(keys, key) } // first key first := "" for i, chr := range keys[0] { if chr == ' ' || chr == '.' || chr == '[' { first += "_" } else { first += string(chr) } if chr == '[' { first += keys[0][i+1:] break } } keys[0] = first // build nested map if err := build(result, keys, value); err != nil { return err } } return nil }