]> Cypherpunks repositories - gostls13.git/commitdiff
json: only use alphanumeric tags
authorRuss Cox <rsc@golang.org>
Thu, 17 Feb 2011 22:14:19 +0000 (17:14 -0500)
committerRuss Cox <rsc@golang.org>
Thu, 17 Feb 2011 22:14:19 +0000 (17:14 -0500)
Almost the same definition as Go identifier names.
(Leading digits are allowed.)

Fixes #1520.

R=r, r2
CC=golang-dev
https://golang.org/cl/4173061

src/pkg/json/decode.go
src/pkg/json/decode_test.go
src/pkg/json/encode.go

index ff91dd83c3360cb2f4acdb699c4965a713eae1bb..388c9a95b0172825029f8cc1319e2ef7f7188766 100644 (file)
@@ -466,13 +466,15 @@ func (d *decodeState) object(v reflect.Value) {
                } else {
                        var f reflect.StructField
                        var ok bool
-                       // First try for field with that tag.
                        st := sv.Type().(*reflect.StructType)
-                       for i := 0; i < sv.NumField(); i++ {
-                               f = st.Field(i)
-                               if f.Tag == key {
-                                       ok = true
-                                       break
+                       // First try for field with that tag.
+                       if isValidTag(key) {
+                               for i := 0; i < sv.NumField(); i++ {
+                                       f = st.Field(i)
+                                       if f.Tag == key {
+                                               ok = true
+                                               break
+                                       }
                                }
                        }
                        if !ok {
index 9cb27af412af2ab59ecdc50510ad3a76e1c43101..2de862c6c220a9e5f44ef838212e0f49cca83576 100644 (file)
@@ -40,6 +40,11 @@ var (
        umtrue   = unmarshaler{true}
 )
 
+type badTag struct {
+       X string
+       Y string "y"
+       Z string "@#*%(#@"
+}
 
 type unmarshalTest struct {
        in  string
@@ -62,6 +67,9 @@ var unmarshalTests = []unmarshalTest{
        {`{"X": [1,2,3], "Y": 4}`, new(T), T{Y: 4}, &UnmarshalTypeError{"array", reflect.Typeof("")}},
        {`{"x": 1}`, new(tx), tx{}, &UnmarshalFieldError{"x", txType, txType.Field(0)}},
 
+       // skip invalid tags
+       {`{"X":"a", "y":"b", "Z":"c"}`, new(badTag), badTag{"a", "b", "c"}, nil},
+
        // syntax errors
        {`{"X": "foo", "Y"}`, nil, nil, SyntaxError("invalid character '}' after object key")},
 
index 0fcc78aa8087991b9fcf6b78db9c054949382095..baaba1a0d59b02ac9c73e99c706ea876d57f65a0 100644 (file)
@@ -13,6 +13,7 @@ import (
        "runtime"
        "sort"
        "strconv"
+       "unicode"
        "utf8"
 )
 
@@ -35,8 +36,9 @@ import (
 //
 // Struct values encode as JSON objects.  Each struct field becomes
 // a member of the object.  By default the object's key name is the
-// struct field name.  If the struct field has a tag, that tag will 
-// be used as the name instead.  Only exported fields will be encoded.
+// struct field name.  If the struct field has a non-empty tag consisting
+// of only Unicode letters, digits, and underscores, that tag will be used
+// as the name instead.  Only exported fields will be encoded.
 //
 // Map values encode as JSON objects.
 // The map's key type must be string; the object keys are used directly
@@ -230,7 +232,7 @@ func (e *encodeState) reflectValue(v reflect.Value) {
                        } else {
                                e.WriteByte(',')
                        }
-                       if f.Tag != "" {
+                       if isValidTag(f.Tag) {
                                e.string(f.Tag)
                        } else {
                                e.string(f.Name)
@@ -285,6 +287,18 @@ func (e *encodeState) reflectValue(v reflect.Value) {
        return
 }
 
+func isValidTag(s string) bool {
+       if s == "" {
+               return false
+       }
+       for _, c := range s {
+               if c != '_' && !unicode.IsLetter(c) && !unicode.IsDigit(c) {
+                       return false
+               }
+       }
+       return true
+}
+
 // stringValues is a slice of reflect.Value holding *reflect.StringValue.
 // It implements the methods to sort by string.
 type stringValues []reflect.Value