mirror of
https://github.com/golang/go
synced 2024-11-25 07:07:57 -07:00
json: add omitempty struct tag option
Fixes #2032 R=rsc, dsymonds, r, r CC=golang-dev https://golang.org/cl/4709044
This commit is contained in:
parent
8d73781f8b
commit
3f53475c97
@ -14,6 +14,7 @@ import (
|
|||||||
"runtime"
|
"runtime"
|
||||||
"sort"
|
"sort"
|
||||||
"strconv"
|
"strconv"
|
||||||
|
"strings"
|
||||||
"unicode"
|
"unicode"
|
||||||
"utf8"
|
"utf8"
|
||||||
)
|
)
|
||||||
@ -36,13 +37,30 @@ import (
|
|||||||
// Array and slice values encode as JSON arrays, except that
|
// Array and slice values encode as JSON arrays, except that
|
||||||
// []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. By default the object's key string
|
// becomes a member of the object unless the field is empty and its tag
|
||||||
// is the struct field name. If the struct field's tag has a "json"
|
// specifies the "omitempty" option. The empty values are false, 0, any
|
||||||
// key with a value that is a non-empty string consisting of only
|
// nil pointer or interface value, and any array, slice, map, or string of
|
||||||
// Unicode letters, digits, dollar signs, hyphens, and underscores,
|
// length zero. The object's default key string is the struct field name
|
||||||
// that value will be used as the object key. For example, the field
|
// but can be specified in the struct field's tag value. The "json" key in
|
||||||
// tag `json:"myName"` says to use "myName" as the object key.
|
// struct field's tag value is the key name, followed by an optional comma
|
||||||
|
// and options. Examples:
|
||||||
|
//
|
||||||
|
// // Specifies that Field appears in JSON as key "myName"
|
||||||
|
// Field int `json:"myName"`
|
||||||
|
//
|
||||||
|
// // Specifies that Field appears in JSON as key "myName" and
|
||||||
|
// // the field is omitted from the object if its value is empty,
|
||||||
|
// // as defined above.
|
||||||
|
// Field int `json:"myName,omitempty"`
|
||||||
|
//
|
||||||
|
// // Field appears in JSON as key "Field" (the default), but
|
||||||
|
// // the field is skipped if empty.
|
||||||
|
// // Note the leading comma.
|
||||||
|
// Field int `json:",omitempty"`
|
||||||
|
//
|
||||||
|
// The key name will be used if it's a non-empty string consisting of
|
||||||
|
// only Unicode letters, digits, dollar signs, hyphens, and underscores.
|
||||||
//
|
//
|
||||||
// Map values encode as JSON objects.
|
// Map values encode as JSON objects.
|
||||||
// The map's key type must be string; the object keys are used directly
|
// The map's key type must be string; the object keys are used directly
|
||||||
@ -184,6 +202,24 @@ func (e *encodeState) error(err os.Error) {
|
|||||||
|
|
||||||
var byteSliceType = reflect.TypeOf([]byte(nil))
|
var byteSliceType = reflect.TypeOf([]byte(nil))
|
||||||
|
|
||||||
|
func isEmptyValue(v reflect.Value) bool {
|
||||||
|
switch v.Kind() {
|
||||||
|
case reflect.Array, reflect.Map, reflect.Slice, reflect.String:
|
||||||
|
return v.Len() == 0
|
||||||
|
case reflect.Bool:
|
||||||
|
return !v.Bool()
|
||||||
|
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
|
||||||
|
return v.Int() == 0
|
||||||
|
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
|
||||||
|
return v.Uint() == 0
|
||||||
|
case reflect.Float32, reflect.Float64:
|
||||||
|
return v.Float() == 0
|
||||||
|
case reflect.Interface, reflect.Ptr:
|
||||||
|
return v.IsNil()
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
func (e *encodeState) reflectValue(v reflect.Value) {
|
func (e *encodeState) reflectValue(v reflect.Value) {
|
||||||
if !v.IsValid() {
|
if !v.IsValid() {
|
||||||
e.WriteString("null")
|
e.WriteString("null")
|
||||||
@ -233,18 +269,30 @@ func (e *encodeState) reflectValue(v reflect.Value) {
|
|||||||
if f.PkgPath != "" {
|
if f.PkgPath != "" {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
tag, omitEmpty := f.Name, false
|
||||||
|
if tv := f.Tag.Get("json"); tv != "" {
|
||||||
|
ss := strings.SplitN(tv, ",", 2)
|
||||||
|
if isValidTag(ss[0]) {
|
||||||
|
tag = ss[0]
|
||||||
|
}
|
||||||
|
if len(ss) > 1 {
|
||||||
|
// Currently the only option is omitempty,
|
||||||
|
// so parsing is trivial.
|
||||||
|
omitEmpty = ss[1] == "omitempty"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
fieldValue := v.Field(i)
|
||||||
|
if omitEmpty && isEmptyValue(fieldValue) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
if first {
|
if first {
|
||||||
first = false
|
first = false
|
||||||
} else {
|
} else {
|
||||||
e.WriteByte(',')
|
e.WriteByte(',')
|
||||||
}
|
}
|
||||||
if tag := f.Tag.Get("json"); tag != "" && isValidTag(tag) {
|
e.string(tag)
|
||||||
e.string(tag)
|
|
||||||
} else {
|
|
||||||
e.string(f.Name)
|
|
||||||
}
|
|
||||||
e.WriteByte(':')
|
e.WriteByte(':')
|
||||||
e.reflectValue(v.Field(i))
|
e.reflectValue(fieldValue)
|
||||||
}
|
}
|
||||||
e.WriteByte('}')
|
e.WriteByte('}')
|
||||||
|
|
||||||
|
44
src/pkg/json/encode_test.go
Normal file
44
src/pkg/json/encode_test.go
Normal file
@ -0,0 +1,44 @@
|
|||||||
|
// Copyright 2011 The Go Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a BSD-style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
package json
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Optionals struct {
|
||||||
|
Sr string `json:"sr"`
|
||||||
|
So string `json:"so,omitempty"`
|
||||||
|
|
||||||
|
Ir int `json:"omitempty"` // actually named omitempty, not an option
|
||||||
|
Io int `json:"io,omitempty"`
|
||||||
|
|
||||||
|
Slr []string `json:"slr,random"`
|
||||||
|
Slo []string `json:"slo,omitempty"`
|
||||||
|
|
||||||
|
Mr map[string]interface{} `json:"mr"`
|
||||||
|
Mo map[string]interface{} `json:",omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
var optionalsExpected = `{
|
||||||
|
"sr": "",
|
||||||
|
"omitempty": 0,
|
||||||
|
"slr": [],
|
||||||
|
"mr": {}
|
||||||
|
}`
|
||||||
|
|
||||||
|
func TestOmitEmpty(t *testing.T) {
|
||||||
|
var o Optionals
|
||||||
|
o.Mr = map[string]interface{}{}
|
||||||
|
o.Mo = map[string]interface{}{}
|
||||||
|
|
||||||
|
got, err := MarshalIndent(&o, "", " ")
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
if got := string(got); got != optionalsExpected {
|
||||||
|
t.Errorf(" got: %s\nwant: %s\n", got, optionalsExpected)
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user