diff --git a/formam.go b/formam.go index 2b5ab1e..25bc2b3 100644 --- a/formam.go +++ b/formam.go @@ -211,65 +211,71 @@ func (dec Decoder) init() error { // analyzePath analyzes the current path to walk through it. // For example: users[0].name func (dec *Decoder) analyzePath() (err error) { - inBracket := false - bracketClosed := false + traversedByBracket := false + nesting := 0 lastPos := 0 - endPos := 0 // parse path for i, char := range []byte(dec.path) { - if char == '[' && inBracket == false { - // found an opening bracket - bracketClosed = false - inBracket = true - dec.field = dec.path[lastPos:i] - lastPos = i + 1 - continue - } else if inBracket { - // it is inside of bracket, so get its value - if char == ']' { - // found an closing bracket, so it will be recently close, so put as true the bracketClosed - // and put as false inBracket and pass the value of bracket to dec.key - inBracket = false - bracketClosed = true - if endPos == 0 { // foo[] without number. - dec.index = dec.path[lastPos:i] - } else { - dec.index = dec.path[lastPos:endPos] - } - lastPos = i + 1 - // traverse the path - err = dec.traverse() - // flush the index already used by traverse - dec.index = "" - // check if the "traverse" failed - if err != nil { - return - } - } else { - // still inside the bracket, so to save the end position - endPos = i + 1 - } - continue - } else if !inBracket { - // not found any bracket, so try found a field - if char == '.' { - // found a field, we need to know if the field is next of a closing bracket, - // for example: [0].Field - if bracketClosed { - bracketClosed = false - lastPos = i + 1 - continue - } - // found a field, but is not next of a closing bracket, for example: Field1.Field2 + switch char { + case '[': + // save current access field + if nesting == 0 { + traversedByBracket = false dec.field = dec.path[lastPos:i] - //dec.field = tmp[:i] lastPos = i + 1 - if err = dec.traverse(); err != nil { - return - } } - continue + nesting += 1 + + case ']': + // no matching open bracket - regular character + if nesting == 0 { + continue + } + + // decrease nesting + nesting -= 1 + // if still inside outer brackets - regular character + // for example: [nested[brackets]] + if nesting > 0 { + continue + } + + traversedByBracket = true + dec.index = dec.path[lastPos:i] + lastPos = i + 1 + // traverse the path + err = dec.traverse() + // flush the index already used by traverse + dec.index = "" + // check if the "traverse" failed + if err != nil { + return + } + + case '.': + // inside brackets - regular character + // for example: [key.with.dots] + if nesting > 0 { + continue + } + + // found a field, we need to know if the field is next to a closing bracket, + // if it is then no need to traverse again + // for example: [0].Field + if traversedByBracket { + traversedByBracket = false + lastPos = i + 1 + continue + } + + // found a field, but is not next to a closing bracket, + // for example: Field1.Field2 + dec.field = dec.path[lastPos:i] + lastPos = i + 1 + if err = dec.traverse(); err != nil { + return + } } } diff --git a/formam_test.go b/formam_test.go index 5dca073..b7fc3f7 100644 --- a/formam_test.go +++ b/formam_test.go @@ -1125,3 +1125,52 @@ func TestMapToPtrStruct(t *testing.T) { t.Errorf("The value in key \"key\" of M is incorrect: %q", v.ID) } } + +func TestBracketsAndNestedPointers(t *testing.T) { + var s struct { + MapStringString map[string]string + MapStringPtrStruct map[string]struct { + ID string + } + MapStringMapStringString map[string]map[string]string + } + + vals := url.Values{ + "MapStringString[a[b][c]d]": []string{"MapStringString[a[b][c]d]"}, + "MapStringString[name.with.dots]": []string{"MapStringString[name.with.dots]"}, + "MapStringPtrStruct[k2]ID": []string{"MapStringPtrStruct[k2]ID"}, + "MapStringMapStringString[a[b[c]d]]q]w": []string{"MapStringMapStringString[a[b[c]d]]q]w"}, + } + + dec := formam.NewDecoder(nil) + + if err := dec.Decode(vals, &s); err != nil { + t.Fatalf("error when decode %s", err) + } + + if v, ok := s.MapStringString["a[b][c]d"]; !ok { + t.Error("The key \"a[b][c]d\" in MapStringString does not exists") + } else if v != "MapStringString[a[b][c]d]" { + t.Error("The value in key \"a[b][c]d\" of MapStringString is incorrect") + } + + if v, ok := s.MapStringString["name.with.dots"]; !ok { + t.Error("The key \"name.with.dots\" in MapStringString does not exists") + } else if v != "MapStringString[name.with.dots]" { + t.Error("The value in key \"name.with.dots\" of MapStringString is incorrect") + } + + if v, ok := s.MapStringPtrStruct["k2"]; !ok { + t.Error("The key \"k2\" in MapStringPtrStruct does not exists") + } else if v.ID != "MapStringPtrStruct[k2]ID" { + t.Error("The value in key \"k2\" of MapStringPtrStruct is incorrect") + } + + if v, ok := s.MapStringMapStringString["a[b[c]d]"]; !ok { + t.Error("The key \"a[b[c]d]\" in MapStringMapStringString does not exists") + } else if vv, ok := v["q]w"]; !ok { + t.Error("The key \"q]w\" in MapStringMapStringString[a[b[c]d]] does not exists") + } else if vv != "MapStringMapStringString[a[b[c]d]]q]w" { + t.Error("The value in key \"q]w\" of MapStringMapStringString[a[b[c]d]] is incorrect") + } +}