mirror of
https://github.com/golang/go
synced 2024-11-20 11:04:56 -07:00
json: preserve field name case by default
This matches the old JSON package behavior. All lowercase names are not as standard as I believed, and it seems less surprising to need to write type T struct { Field string "field" } to get lower case (behavior after this CL) than it does to need to write type T struct { Field string "Field" } to preserve the case (behavior before this CL). Also test and fix unmarshal into non-nil interface value or pointer. Fixes #744. R=r CC=golang-dev https://golang.org/cl/1013041
This commit is contained in:
parent
cc62bed075
commit
bec40ba516
@ -20,7 +20,7 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
// Unmarshal parses the JSON-encoded data and stores the result
|
// Unmarshal parses the JSON-encoded data and stores the result
|
||||||
// in the value pointed at by v.
|
// in the value pointed to by v.
|
||||||
//
|
//
|
||||||
// Unmarshal traverses the value v recursively.
|
// Unmarshal traverses the value v recursively.
|
||||||
// If an encountered value implements the Unmarshaler interface,
|
// If an encountered value implements the Unmarshaler interface,
|
||||||
@ -247,6 +247,10 @@ func (d *decodeState) indirect(v reflect.Value, wantptr bool) (Unmarshaler, refl
|
|||||||
_, isUnmarshaler = v.Interface().(Unmarshaler)
|
_, isUnmarshaler = v.Interface().(Unmarshaler)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if iv, ok := v.(*reflect.InterfaceValue); ok && !iv.IsNil() {
|
||||||
|
v = iv.Elem()
|
||||||
|
continue
|
||||||
|
}
|
||||||
pv, ok := v.(*reflect.PtrValue)
|
pv, ok := v.(*reflect.PtrValue)
|
||||||
if !ok {
|
if !ok {
|
||||||
break
|
break
|
||||||
@ -255,7 +259,9 @@ func (d *decodeState) indirect(v reflect.Value, wantptr bool) (Unmarshaler, refl
|
|||||||
if !isptrptr && wantptr && !isUnmarshaler {
|
if !isptrptr && wantptr && !isUnmarshaler {
|
||||||
return nil, pv
|
return nil, pv
|
||||||
}
|
}
|
||||||
pv.PointTo(reflect.MakeZero(pv.Type().(*reflect.PtrType).Elem()))
|
if pv.IsNil() {
|
||||||
|
pv.PointTo(reflect.MakeZero(pv.Type().(*reflect.PtrType).Elem()))
|
||||||
|
}
|
||||||
if isUnmarshaler {
|
if isUnmarshaler {
|
||||||
// Using v.Interface().(Unmarshaler)
|
// Using v.Interface().(Unmarshaler)
|
||||||
// here means that we have to use a pointer
|
// here means that we have to use a pointer
|
||||||
@ -436,11 +442,12 @@ func (d *decodeState) object(v reflect.Value) {
|
|||||||
d.error(errPhase)
|
d.error(errPhase)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Figure out
|
// Figure out field corresponding to key.
|
||||||
var subv reflect.Value
|
var subv reflect.Value
|
||||||
if mv != nil {
|
if mv != nil {
|
||||||
subv = reflect.MakeZero(mv.Type().(*reflect.MapType).Elem())
|
subv = reflect.MakeZero(mv.Type().(*reflect.MapType).Elem())
|
||||||
} else {
|
} else {
|
||||||
|
// First try for field with that tag.
|
||||||
for i := 0; i < sv.NumField(); i++ {
|
for i := 0; i < sv.NumField(); i++ {
|
||||||
f := sv.Type().(*reflect.StructType).Field(i)
|
f := sv.Type().(*reflect.StructType).Field(i)
|
||||||
if f.Tag == key {
|
if f.Tag == key {
|
||||||
@ -449,7 +456,12 @@ func (d *decodeState) object(v reflect.Value) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
if subv == nil {
|
if subv == nil {
|
||||||
subv = sv.FieldByNameFunc(func(s string) bool { return matchName(key, s) })
|
// Second, exact match.
|
||||||
|
subv = sv.FieldByName(key)
|
||||||
|
if subv == nil {
|
||||||
|
// Third, case-insensitive match.
|
||||||
|
subv = sv.FieldByNameFunc(func(s string) bool { return matchName(key, s) })
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -104,6 +104,32 @@ func TestUnmarshalMarshal(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type Xint struct {
|
||||||
|
X int
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestUnmarshalInterface(t *testing.T) {
|
||||||
|
var xint Xint
|
||||||
|
var i interface{} = &xint
|
||||||
|
if err := Unmarshal([]byte(`{"X":1}`), &i); err != nil {
|
||||||
|
t.Fatalf("Unmarshal: %v", err)
|
||||||
|
}
|
||||||
|
if xint.X != 1 {
|
||||||
|
t.Fatalf("Did not write to xint")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestUnmarshalPtrPtr(t *testing.T) {
|
||||||
|
var xint Xint
|
||||||
|
pxint := &xint
|
||||||
|
if err := Unmarshal([]byte(`{"X":1}`), &pxint); err != nil {
|
||||||
|
t.Fatalf("Unmarshal: %v", err)
|
||||||
|
}
|
||||||
|
if xint.X != 1 {
|
||||||
|
t.Fatalf("Did not write to xint")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func noSpace(c int) int {
|
func noSpace(c int) int {
|
||||||
if isSpace(c) {
|
if isSpace(c) {
|
||||||
return -1
|
return -1
|
||||||
@ -243,185 +269,185 @@ var pallValue = All{
|
|||||||
}
|
}
|
||||||
|
|
||||||
var allValueIndent = `{
|
var allValueIndent = `{
|
||||||
"bool": true,
|
"Bool": true,
|
||||||
"int": 2,
|
"Int": 2,
|
||||||
"int8": 3,
|
"Int8": 3,
|
||||||
"int16": 4,
|
"Int16": 4,
|
||||||
"int32": 5,
|
"Int32": 5,
|
||||||
"int64": 6,
|
"Int64": 6,
|
||||||
"uint": 7,
|
"Uint": 7,
|
||||||
"uint8": 8,
|
"Uint8": 8,
|
||||||
"uint16": 9,
|
"Uint16": 9,
|
||||||
"uint32": 10,
|
"Uint32": 10,
|
||||||
"uint64": 11,
|
"Uint64": 11,
|
||||||
"uintptr": 12,
|
"Uintptr": 12,
|
||||||
"float": 13.1,
|
"Float": 13.1,
|
||||||
"float32": 14.1,
|
"Float32": 14.1,
|
||||||
"float64": 15.1,
|
"Float64": 15.1,
|
||||||
"bar": "foo",
|
"bar": "foo",
|
||||||
"pbool": null,
|
"PBool": null,
|
||||||
"pint": null,
|
"PInt": null,
|
||||||
"pint8": null,
|
"PInt8": null,
|
||||||
"pint16": null,
|
"PInt16": null,
|
||||||
"pint32": null,
|
"PInt32": null,
|
||||||
"pint64": null,
|
"PInt64": null,
|
||||||
"puint": null,
|
"PUint": null,
|
||||||
"puint8": null,
|
"PUint8": null,
|
||||||
"puint16": null,
|
"PUint16": null,
|
||||||
"puint32": null,
|
"PUint32": null,
|
||||||
"puint64": null,
|
"PUint64": null,
|
||||||
"puintptr": null,
|
"PUintptr": null,
|
||||||
"pfloat": null,
|
"PFloat": null,
|
||||||
"pfloat32": null,
|
"PFloat32": null,
|
||||||
"pfloat64": null,
|
"PFloat64": null,
|
||||||
"string": "16",
|
"String": "16",
|
||||||
"pstring": null,
|
"PString": null,
|
||||||
"map": {
|
"Map": {
|
||||||
"17": {
|
"17": {
|
||||||
"tag": "tag17"
|
"Tag": "tag17"
|
||||||
},
|
},
|
||||||
"18": {
|
"18": {
|
||||||
"tag": "tag18"
|
"Tag": "tag18"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"mapp": {
|
"MapP": {
|
||||||
"19": {
|
"19": {
|
||||||
"tag": "tag19"
|
"Tag": "tag19"
|
||||||
},
|
},
|
||||||
"20": null
|
"20": null
|
||||||
},
|
},
|
||||||
"pmap": null,
|
"PMap": null,
|
||||||
"pmapp": null,
|
"PMapP": null,
|
||||||
"emptymap": {},
|
"EmptyMap": {},
|
||||||
"nilmap": null,
|
"NilMap": null,
|
||||||
"slice": [
|
"Slice": [
|
||||||
{
|
{
|
||||||
"tag": "tag20"
|
"Tag": "tag20"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"tag": "tag21"
|
"Tag": "tag21"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"slicep": [
|
"SliceP": [
|
||||||
{
|
{
|
||||||
"tag": "tag22"
|
"Tag": "tag22"
|
||||||
},
|
},
|
||||||
null,
|
null,
|
||||||
{
|
{
|
||||||
"tag": "tag23"
|
"Tag": "tag23"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"pslice": null,
|
"PSlice": null,
|
||||||
"pslicep": null,
|
"PSliceP": null,
|
||||||
"emptyslice": [],
|
"EmptySlice": [],
|
||||||
"nilslice": [],
|
"NilSlice": [],
|
||||||
"stringslice": [
|
"StringSlice": [
|
||||||
"str24",
|
"str24",
|
||||||
"str25",
|
"str25",
|
||||||
"str26"
|
"str26"
|
||||||
],
|
],
|
||||||
"byteslice": [
|
"ByteSlice": [
|
||||||
27,
|
27,
|
||||||
28,
|
28,
|
||||||
29
|
29
|
||||||
],
|
],
|
||||||
"small": {
|
"Small": {
|
||||||
"tag": "tag30"
|
"Tag": "tag30"
|
||||||
},
|
},
|
||||||
"psmall": {
|
"PSmall": {
|
||||||
"tag": "tag31"
|
"Tag": "tag31"
|
||||||
},
|
},
|
||||||
"ppsmall": null,
|
"PPSmall": null,
|
||||||
"interface": 5.2,
|
"Interface": 5.2,
|
||||||
"pinterface": null
|
"PInterface": null
|
||||||
}`
|
}`
|
||||||
|
|
||||||
var allValueCompact = strings.Map(noSpace, allValueIndent)
|
var allValueCompact = strings.Map(noSpace, allValueIndent)
|
||||||
|
|
||||||
var pallValueIndent = `{
|
var pallValueIndent = `{
|
||||||
"bool": false,
|
"Bool": false,
|
||||||
"int": 0,
|
"Int": 0,
|
||||||
"int8": 0,
|
"Int8": 0,
|
||||||
"int16": 0,
|
"Int16": 0,
|
||||||
"int32": 0,
|
"Int32": 0,
|
||||||
"int64": 0,
|
"Int64": 0,
|
||||||
"uint": 0,
|
"Uint": 0,
|
||||||
"uint8": 0,
|
"Uint8": 0,
|
||||||
"uint16": 0,
|
"Uint16": 0,
|
||||||
"uint32": 0,
|
"Uint32": 0,
|
||||||
"uint64": 0,
|
"Uint64": 0,
|
||||||
"uintptr": 0,
|
"Uintptr": 0,
|
||||||
"float": 0,
|
"Float": 0,
|
||||||
"float32": 0,
|
"Float32": 0,
|
||||||
"float64": 0,
|
"Float64": 0,
|
||||||
"bar": "",
|
"bar": "",
|
||||||
"pbool": true,
|
"PBool": true,
|
||||||
"pint": 2,
|
"PInt": 2,
|
||||||
"pint8": 3,
|
"PInt8": 3,
|
||||||
"pint16": 4,
|
"PInt16": 4,
|
||||||
"pint32": 5,
|
"PInt32": 5,
|
||||||
"pint64": 6,
|
"PInt64": 6,
|
||||||
"puint": 7,
|
"PUint": 7,
|
||||||
"puint8": 8,
|
"PUint8": 8,
|
||||||
"puint16": 9,
|
"PUint16": 9,
|
||||||
"puint32": 10,
|
"PUint32": 10,
|
||||||
"puint64": 11,
|
"PUint64": 11,
|
||||||
"puintptr": 12,
|
"PUintptr": 12,
|
||||||
"pfloat": 13.1,
|
"PFloat": 13.1,
|
||||||
"pfloat32": 14.1,
|
"PFloat32": 14.1,
|
||||||
"pfloat64": 15.1,
|
"PFloat64": 15.1,
|
||||||
"string": "",
|
"String": "",
|
||||||
"pstring": "16",
|
"PString": "16",
|
||||||
"map": null,
|
"Map": null,
|
||||||
"mapp": null,
|
"MapP": null,
|
||||||
"pmap": {
|
"PMap": {
|
||||||
"17": {
|
"17": {
|
||||||
"tag": "tag17"
|
"Tag": "tag17"
|
||||||
},
|
},
|
||||||
"18": {
|
"18": {
|
||||||
"tag": "tag18"
|
"Tag": "tag18"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"pmapp": {
|
"PMapP": {
|
||||||
"19": {
|
"19": {
|
||||||
"tag": "tag19"
|
"Tag": "tag19"
|
||||||
},
|
},
|
||||||
"20": null
|
"20": null
|
||||||
},
|
},
|
||||||
"emptymap": null,
|
"EmptyMap": null,
|
||||||
"nilmap": null,
|
"NilMap": null,
|
||||||
"slice": [],
|
"Slice": [],
|
||||||
"slicep": [],
|
"SliceP": [],
|
||||||
"pslice": [
|
"PSlice": [
|
||||||
{
|
{
|
||||||
"tag": "tag20"
|
"Tag": "tag20"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"tag": "tag21"
|
"Tag": "tag21"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"pslicep": [
|
"PSliceP": [
|
||||||
{
|
{
|
||||||
"tag": "tag22"
|
"Tag": "tag22"
|
||||||
},
|
},
|
||||||
null,
|
null,
|
||||||
{
|
{
|
||||||
"tag": "tag23"
|
"Tag": "tag23"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"emptyslice": [],
|
"EmptySlice": [],
|
||||||
"nilslice": [],
|
"NilSlice": [],
|
||||||
"stringslice": [],
|
"StringSlice": [],
|
||||||
"byteslice": [],
|
"ByteSlice": [],
|
||||||
"small": {
|
"Small": {
|
||||||
"tag": ""
|
"Tag": ""
|
||||||
},
|
},
|
||||||
"psmall": null,
|
"PSmall": null,
|
||||||
"ppsmall": {
|
"PPSmall": {
|
||||||
"tag": "tag31"
|
"Tag": "tag31"
|
||||||
},
|
},
|
||||||
"interface": null,
|
"Interface": null,
|
||||||
"pinterface": 5.2
|
"PInterface": 5.2
|
||||||
}`
|
}`
|
||||||
|
|
||||||
var pallValueCompact = strings.Map(noSpace, pallValueIndent)
|
var pallValueCompact = strings.Map(noSpace, pallValueIndent)
|
||||||
|
@ -11,7 +11,6 @@ import (
|
|||||||
"runtime"
|
"runtime"
|
||||||
"sort"
|
"sort"
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// Marshal returns the JSON encoding of v.
|
// Marshal returns the JSON encoding of v.
|
||||||
@ -40,7 +39,7 @@ import (
|
|||||||
// 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
|
||||||
// as map keys.
|
// as map keys.
|
||||||
//
|
//
|
||||||
// Pointer values encode as the value pointed at.
|
// Pointer values encode as the value pointed to.
|
||||||
// A nil pointer encodes as the null JSON object.
|
// A nil pointer encodes as the null JSON object.
|
||||||
//
|
//
|
||||||
// Interface values encode as the value contained in the interface.
|
// Interface values encode as the value contained in the interface.
|
||||||
@ -202,7 +201,7 @@ func (e *encodeState) reflectValue(v reflect.Value) {
|
|||||||
if f.Tag != "" {
|
if f.Tag != "" {
|
||||||
e.string(f.Tag)
|
e.string(f.Tag)
|
||||||
} else {
|
} else {
|
||||||
e.string(strings.ToLower(f.Name))
|
e.string(f.Name)
|
||||||
}
|
}
|
||||||
e.WriteByte(':')
|
e.WriteByte(':')
|
||||||
e.reflectValue(v.Field(i))
|
e.reflectValue(v.Field(i))
|
||||||
|
Loading…
Reference in New Issue
Block a user