} 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 {
umtrue = unmarshaler{true}
)
+type badTag struct {
+ X string
+ Y string "y"
+ Z string "@#*%(#@"
+}
type unmarshalTest struct {
in string
{`{"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")},
"runtime"
"sort"
"strconv"
+ "unicode"
"utf8"
)
//
// 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
} else {
e.WriteByte(',')
}
- if f.Tag != "" {
+ if isValidTag(f.Tag) {
e.string(f.Tag)
} else {
e.string(f.Name)
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