]> Cypherpunks repositories - gostls13.git/commitdiff
reflect: support multiple keys in struct tags
authorAlexey Vilenskiy <bynovhack@gmail.com>
Fri, 14 Aug 2020 08:37:31 +0000 (11:37 +0300)
committerIan Lance Taylor <iant@golang.org>
Tue, 6 Oct 2020 00:34:55 +0000 (00:34 +0000)
Fixes #40281

Change-Id: Ie624bce3a78a06d7ed71bba1f501e66802dffd13
Reviewed-on: https://go-review.googlesource.com/c/go/+/248341
Reviewed-by: Ian Lance Taylor <iant@golang.org>
Trust: Dmitri Shuralyov <dmitshur@golang.org>

src/reflect/all_test.go
src/reflect/type.go

index 0684eab973dbdf6a3da33e268a2b039e4b79d87f..a12712d2540f611968380f9d19320ee2f634b240 100644 (file)
@@ -7165,6 +7165,176 @@ func TestMapIterDelete1(t *testing.T) {
        }
 }
 
+func TestStructTagLookup(t *testing.T) {
+       var tests = []struct {
+               tag           StructTag
+               key           string
+               expectedValue string
+               expectedOK    bool
+       }{
+               {
+                       tag:           `json:"json_value_1"`,
+                       key:           "json",
+                       expectedValue: "json_value_1",
+                       expectedOK:    true,
+               },
+               {
+                       tag:           `json:"json_value_2" xml:"xml_value_2"`,
+                       key:           "json",
+                       expectedValue: "json_value_2",
+                       expectedOK:    true,
+               },
+               {
+                       tag:           `json:"json_value_3" xml:"xml_value_3"`,
+                       key:           "xml",
+                       expectedValue: "xml_value_3",
+                       expectedOK:    true,
+               },
+               {
+                       tag:           `bson json:"shared_value_4"`,
+                       key:           "json",
+                       expectedValue: "shared_value_4",
+                       expectedOK:    true,
+               },
+               {
+                       tag:           `bson json:"shared_value_5"`,
+                       key:           "bson",
+                       expectedValue: "shared_value_5",
+                       expectedOK:    true,
+               },
+               {
+                       tag:           `json bson xml form:"field_1,omitempty" other:"value_1"`,
+                       key:           "xml",
+                       expectedValue: "field_1,omitempty",
+                       expectedOK:    true,
+               },
+               {
+                       tag:           `json bson xml form:"field_2,omitempty" other:"value_2"`,
+                       key:           "form",
+                       expectedValue: "field_2,omitempty",
+                       expectedOK:    true,
+               },
+               {
+                       tag:           `json bson xml form:"field_3,omitempty" other:"value_3"`,
+                       key:           "other",
+                       expectedValue: "value_3",
+                       expectedOK:    true,
+               },
+               {
+                       tag:           `json    bson    xml    form:"field_4" other:"value_4"`,
+                       key:           "json",
+                       expectedValue: "field_4",
+                       expectedOK:    true,
+               },
+               {
+                       tag:           `json    bson    xml    form:"field_5" other:"value_5"`,
+                       key:           "non_existing",
+                       expectedValue: "",
+                       expectedOK:    false,
+               },
+               {
+                       tag:           `json "json_6"`,
+                       key:           "json",
+                       expectedValue: "",
+                       expectedOK:    false,
+               },
+               {
+                       tag:           `json:"json_7" bson "bson_7"`,
+                       key:           "json",
+                       expectedValue: "json_7",
+                       expectedOK:    true,
+               },
+               {
+                       tag:           `json:"json_8" xml "xml_8"`,
+                       key:           "xml",
+                       expectedValue: "",
+                       expectedOK:    false,
+               },
+               {
+                       tag:           `json    bson    xml    form "form_9" other:"value_9"`,
+                       key:           "bson",
+                       expectedValue: "",
+                       expectedOK:    false,
+               },
+               {
+                       tag:           `json bson xml form "form_10" other:"value_10"`,
+                       key:           "other",
+                       expectedValue: "",
+                       expectedOK:    false,
+               },
+               {
+                       tag:           `json bson xml form:"form_11" other "value_11"`,
+                       key:           "json",
+                       expectedValue: "form_11",
+                       expectedOK:    true,
+               },
+               {
+                       tag:           `tag1`,
+                       key:           "tag1",
+                       expectedValue: "",
+                       expectedOK:    false,
+               },
+               {
+                       tag:           `tag2 :"hello_2"`,
+                       key:           "tag2",
+                       expectedValue: "",
+                       expectedOK:    false,
+               },
+               {
+                       tag:           `tag3: "hello_3"`,
+                       key:           "tag3",
+                       expectedValue: "",
+                       expectedOK:    false,
+               },
+               {
+                       tag:           "json\x7fbson: \"hello_4\"",
+                       key:           "json",
+                       expectedValue: "",
+                       expectedOK:    false,
+               },
+               {
+                       tag:           "json\x7fbson: \"hello_5\"",
+                       key:           "bson",
+                       expectedValue: "",
+                       expectedOK:    false,
+               },
+               {
+                       tag:           "json bson:\x7f\"hello_6\"",
+                       key:           "json",
+                       expectedValue: "",
+                       expectedOK:    false,
+               },
+               {
+                       tag:           "json bson:\x7f\"hello_7\"",
+                       key:           "bson",
+                       expectedValue: "",
+                       expectedOK:    false,
+               },
+               {
+                       tag:           "json\x09bson:\"hello_8\"",
+                       key:           "json",
+                       expectedValue: "",
+                       expectedOK:    false,
+               },
+               {
+                       tag:           "a\x7fb json:\"val\"",
+                       key:           "json",
+                       expectedValue: "",
+                       expectedOK:    false,
+               },
+       }
+
+       for _, test := range tests {
+               v, ok := test.tag.Lookup(test.key)
+               if v != test.expectedValue {
+                       t.Errorf("struct tag lookup failed, got %s, want %s", v, test.expectedValue)
+               }
+               if ok != test.expectedOK {
+                       t.Errorf("struct tag lookup failed, got %t, want %t", ok, test.expectedOK)
+               }
+       }
+}
+
 // iterateToString returns the set of elements
 // returned by an iterator in readable form.
 func iterateToString(it *MapIter) string {
index 44c96fea8282a91cd6bb68fdc178dd7d8f140770..a3a616701b9abf61c53664d82f1e85884a2ca0ab 100644 (file)
@@ -1130,6 +1130,9 @@ func (tag StructTag) Lookup(key string) (value string, ok bool) {
        // When modifying this code, also update the validateStructTag code
        // in cmd/vet/structtag.go.
 
+       // keyFound indicates that such key on the left side has already been found.
+       var keyFound bool
+
        for tag != "" {
                // Skip leading space.
                i := 0
@@ -1149,11 +1152,29 @@ func (tag StructTag) Lookup(key string) (value string, ok bool) {
                for i < len(tag) && tag[i] > ' ' && tag[i] != ':' && tag[i] != '"' && tag[i] != 0x7f {
                        i++
                }
-               if i == 0 || i+1 >= len(tag) || tag[i] != ':' || tag[i+1] != '"' {
+               if i == 0 || i+1 >= len(tag) || tag[i] < ' ' || tag[i] == 0x7f {
                        break
                }
                name := string(tag[:i])
-               tag = tag[i+1:]
+               tag = tag[i:]
+
+               // If we found a space char here - assume that we have a tag with
+               // multiple keys.
+               if tag[0] == ' ' {
+                       if name == key {
+                               keyFound = true
+                       }
+                       continue
+               }
+
+               // Spaces were filtered above so we assume that here we have
+               // only valid tag value started with `:"`.
+               if tag[0] != ':' || tag[1] != '"' {
+                       break
+               }
+
+               // Remove the colon leaving tag at the start of the quoted string.
+               tag = tag[1:]
 
                // Scan quoted string to find value.
                i = 1
@@ -1169,7 +1190,7 @@ func (tag StructTag) Lookup(key string) (value string, ok bool) {
                qvalue := string(tag[:i+1])
                tag = tag[i+1:]
 
-               if key == name {
+               if key == name || keyFound {
                        value, err := strconv.Unquote(qvalue)
                        if err != nil {
                                break