Commit 3be088e3 authored by David Symonds's avatar David Symonds

json: if a field's tag is "-", never encode it.

R=adg, r, edsrzf, rsc, r
CC=golang-dev
https://golang.org/cl/4962052
parent 23fab11c
...@@ -391,11 +391,6 @@ func (d *decodeState) array(v reflect.Value) { ...@@ -391,11 +391,6 @@ func (d *decodeState) array(v reflect.Value) {
} }
} }
// matchName returns true if key should be written to a field named name.
func matchName(key, name string) bool {
return strings.ToLower(key) == strings.ToLower(name)
}
// object consumes an object from d.data[d.off-1:], decoding into the value v. // object consumes an object from d.data[d.off-1:], decoding into the value v.
// the first byte of the object ('{') has been read already. // the first byte of the object ('{') has been read already.
func (d *decodeState) object(v reflect.Value) { func (d *decodeState) object(v reflect.Value) {
...@@ -485,24 +480,31 @@ func (d *decodeState) object(v reflect.Value) { ...@@ -485,24 +480,31 @@ func (d *decodeState) object(v reflect.Value) {
var f reflect.StructField var f reflect.StructField
var ok bool var ok bool
st := sv.Type() st := sv.Type()
// First try for field with that tag.
if isValidTag(key) {
for i := 0; i < sv.NumField(); i++ { for i := 0; i < sv.NumField(); i++ {
f = st.Field(i) sf := st.Field(i)
tagName, _ := parseTag(f.Tag.Get("json")) tag := sf.Tag.Get("json")
if tag == "-" {
// Pretend this field doesn't exist.
continue
}
// First, tag match
tagName, _ := parseTag(tag)
if tagName == key { if tagName == key {
f = sf
ok = true ok = true
break break // no better match possible
}
} }
// Second, exact field name match
if sf.Name == key {
f = sf
ok = true
} }
if !ok { // Third, case-insensitive field name match,
// Second, exact match. // but only if a better match hasn't already been seen
f, ok = st.FieldByName(key) if !ok && strings.ToLower(sf.Name) == strings.ToLower(key) {
f = sf
ok = true
} }
if !ok {
// Third, case-insensitive match.
f, ok = st.FieldByNameFunc(func(s string) bool { return matchName(key, s) })
} }
// Extract value; name must be exported. // Extract value; name must be exported.
......
...@@ -15,6 +15,7 @@ import ( ...@@ -15,6 +15,7 @@ import (
type T struct { type T struct {
X string X string
Y int Y int
Z int `json:"-"`
} }
type tx struct { type tx struct {
...@@ -68,6 +69,9 @@ var unmarshalTests = []unmarshalTest{ ...@@ -68,6 +69,9 @@ var unmarshalTests = []unmarshalTest{
{`{"X": [1,2,3], "Y": 4}`, new(T), T{Y: 4}, &UnmarshalTypeError{"array", reflect.TypeOf("")}}, {`{"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)}}, {`{"x": 1}`, new(tx), tx{}, &UnmarshalFieldError{"x", txType, txType.Field(0)}},
// Z has a "-" tag.
{`{"Y": 1, "Z": 2}`, new(T), T{Y: 1}, nil},
// syntax errors // syntax errors
{`{"X": "foo", "Y"}`, nil, nil, &SyntaxError{"invalid character '}' after object key", 17}}, {`{"X": "foo", "Y"}`, nil, nil, &SyntaxError{"invalid character '}' after object key", 17}},
......
...@@ -40,18 +40,23 @@ import ( ...@@ -40,18 +40,23 @@ import (
// []byte encodes as a base64-encoded string. // []byte encodes as a base64-encoded string.
// //
// Struct values encode as JSON objects. Each exported struct field // Struct values encode as JSON objects. Each exported struct field
// becomes a member of the object unless the field is empty and its tag // becomes a member of the object unless
// specifies the "omitempty" option. The empty values are false, 0, any // - the field's tag is "-", or
// - the field is empty and its tag specifies the "omitempty" option.
// The empty values are false, 0, any
// nil pointer or interface value, and any array, slice, map, or string of // nil pointer or interface value, and any array, slice, map, or string of
// length zero. The object's default key string is the struct field name // length zero. The object's default key string is the struct field name
// but can be specified in the struct field's tag value. The "json" key in // but can be specified in the struct field's tag value. The "json" key in
// struct field's tag value is the key name, followed by an optional comma // struct field's tag value is the key name, followed by an optional comma
// and options. Examples: // and options. Examples:
// //
// // Specifies that Field appears in JSON as key "myName" // // Field is ignored by this package.
// Field int `json:"-"`
//
// // Field appears in JSON as key "myName".
// Field int `json:"myName"` // Field int `json:"myName"`
// //
// // Specifies that Field appears in JSON as key "myName" and // // Field appears in JSON as key "myName" and
// // the field is omitted from the object if its value is empty, // // the field is omitted from the object if its value is empty,
// // as defined above. // // as defined above.
// Field int `json:"myName,omitempty"` // Field int `json:"myName,omitempty"`
...@@ -298,6 +303,9 @@ func (e *encodeState) reflectValueQuoted(v reflect.Value, quoted bool) { ...@@ -298,6 +303,9 @@ func (e *encodeState) reflectValueQuoted(v reflect.Value, quoted bool) {
} }
tag, omitEmpty, quoted := f.Name, false, false tag, omitEmpty, quoted := f.Name, false, false
if tv := f.Tag.Get("json"); tv != "" { if tv := f.Tag.Get("json"); tv != "" {
if tv == "-" {
continue
}
name, opts := parseTag(tv) name, opts := parseTag(tv)
if isValidTag(name) { if isValidTag(name) {
tag = name tag = name
......
...@@ -13,6 +13,7 @@ import ( ...@@ -13,6 +13,7 @@ import (
type Optionals struct { type Optionals struct {
Sr string `json:"sr"` Sr string `json:"sr"`
So string `json:"so,omitempty"` So string `json:"so,omitempty"`
Sw string `json:"-"`
Ir int `json:"omitempty"` // actually named omitempty, not an option Ir int `json:"omitempty"` // actually named omitempty, not an option
Io int `json:"io,omitempty"` Io int `json:"io,omitempty"`
...@@ -33,6 +34,7 @@ var optionalsExpected = `{ ...@@ -33,6 +34,7 @@ var optionalsExpected = `{
func TestOmitEmpty(t *testing.T) { func TestOmitEmpty(t *testing.T) {
var o Optionals var o Optionals
o.Sw = "something"
o.Mr = map[string]interface{}{} o.Mr = map[string]interface{}{}
o.Mo = map[string]interface{}{} o.Mo = map[string]interface{}{}
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment