Commit bec40ba5 authored by Russ Cox's avatar Russ Cox

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
parent cc62bed0
......@@ -20,7 +20,7 @@ import (
)
// 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.
// 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)
}
if iv, ok := v.(*reflect.InterfaceValue); ok && !iv.IsNil() {
v = iv.Elem()
continue
}
pv, ok := v.(*reflect.PtrValue)
if !ok {
break
......@@ -255,7 +259,9 @@ func (d *decodeState) indirect(v reflect.Value, wantptr bool) (Unmarshaler, refl
if !isptrptr && wantptr && !isUnmarshaler {
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 {
// Using v.Interface().(Unmarshaler)
// here means that we have to use a pointer
......@@ -436,11 +442,12 @@ func (d *decodeState) object(v reflect.Value) {
d.error(errPhase)
}
// Figure out
// Figure out field corresponding to key.
var subv reflect.Value
if mv != nil {
subv = reflect.MakeZero(mv.Type().(*reflect.MapType).Elem())
} else {
// First try for field with that tag.
for i := 0; i < sv.NumField(); i++ {
f := sv.Type().(*reflect.StructType).Field(i)
if f.Tag == key {
......@@ -449,7 +456,12 @@ func (d *decodeState) object(v reflect.Value) {
}
}
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 {
if isSpace(c) {
return -1
......@@ -243,185 +269,185 @@ var pallValue = All{
}
var allValueIndent = `{
"bool": true,
"int": 2,
"int8": 3,
"int16": 4,
"int32": 5,
"int64": 6,
"uint": 7,
"uint8": 8,
"uint16": 9,
"uint32": 10,
"uint64": 11,
"uintptr": 12,
"float": 13.1,
"float32": 14.1,
"float64": 15.1,
"Bool": true,
"Int": 2,
"Int8": 3,
"Int16": 4,
"Int32": 5,
"Int64": 6,
"Uint": 7,
"Uint8": 8,
"Uint16": 9,
"Uint32": 10,
"Uint64": 11,
"Uintptr": 12,
"Float": 13.1,
"Float32": 14.1,
"Float64": 15.1,
"bar": "foo",
"pbool": null,
"pint": null,
"pint8": null,
"pint16": null,
"pint32": null,
"pint64": null,
"puint": null,
"puint8": null,
"puint16": null,
"puint32": null,
"puint64": null,
"puintptr": null,
"pfloat": null,
"pfloat32": null,
"pfloat64": null,
"string": "16",
"pstring": null,
"map": {
"PBool": null,
"PInt": null,
"PInt8": null,
"PInt16": null,
"PInt32": null,
"PInt64": null,
"PUint": null,
"PUint8": null,
"PUint16": null,
"PUint32": null,
"PUint64": null,
"PUintptr": null,
"PFloat": null,
"PFloat32": null,
"PFloat64": null,
"String": "16",
"PString": null,
"Map": {
"17": {
"tag": "tag17"
"Tag": "tag17"
},
"18": {
"tag": "tag18"
"Tag": "tag18"
}
},
"mapp": {
"MapP": {
"19": {
"tag": "tag19"
"Tag": "tag19"
},
"20": null
},
"pmap": null,
"pmapp": null,
"emptymap": {},
"nilmap": null,
"slice": [
"PMap": null,
"PMapP": null,
"EmptyMap": {},
"NilMap": null,
"Slice": [
{
"tag": "tag20"
"Tag": "tag20"
},
{
"tag": "tag21"
"Tag": "tag21"
}
],
"slicep": [
"SliceP": [
{
"tag": "tag22"
"Tag": "tag22"
},
null,
{
"tag": "tag23"
"Tag": "tag23"
}
],
"pslice": null,
"pslicep": null,
"emptyslice": [],
"nilslice": [],
"stringslice": [
"PSlice": null,
"PSliceP": null,
"EmptySlice": [],
"NilSlice": [],
"StringSlice": [
"str24",
"str25",
"str26"
],
"byteslice": [
"ByteSlice": [
27,
28,
29
],
"small": {
"tag": "tag30"
"Small": {
"Tag": "tag30"
},
"psmall": {
"tag": "tag31"
"PSmall": {
"Tag": "tag31"
},
"ppsmall": null,
"interface": 5.2,
"pinterface": null
"PPSmall": null,
"Interface": 5.2,
"PInterface": null
}`
var allValueCompact = strings.Map(noSpace, allValueIndent)
var pallValueIndent = `{
"bool": false,
"int": 0,
"int8": 0,
"int16": 0,
"int32": 0,
"int64": 0,
"uint": 0,
"uint8": 0,
"uint16": 0,
"uint32": 0,
"uint64": 0,
"uintptr": 0,
"float": 0,
"float32": 0,
"float64": 0,
"Bool": false,
"Int": 0,
"Int8": 0,
"Int16": 0,
"Int32": 0,
"Int64": 0,
"Uint": 0,
"Uint8": 0,
"Uint16": 0,
"Uint32": 0,
"Uint64": 0,
"Uintptr": 0,
"Float": 0,
"Float32": 0,
"Float64": 0,
"bar": "",
"pbool": true,
"pint": 2,
"pint8": 3,
"pint16": 4,
"pint32": 5,
"pint64": 6,
"puint": 7,
"puint8": 8,
"puint16": 9,
"puint32": 10,
"puint64": 11,
"puintptr": 12,
"pfloat": 13.1,
"pfloat32": 14.1,
"pfloat64": 15.1,
"string": "",
"pstring": "16",
"map": null,
"mapp": null,
"pmap": {
"PBool": true,
"PInt": 2,
"PInt8": 3,
"PInt16": 4,
"PInt32": 5,
"PInt64": 6,
"PUint": 7,
"PUint8": 8,
"PUint16": 9,
"PUint32": 10,
"PUint64": 11,
"PUintptr": 12,
"PFloat": 13.1,
"PFloat32": 14.1,
"PFloat64": 15.1,
"String": "",
"PString": "16",
"Map": null,
"MapP": null,
"PMap": {
"17": {
"tag": "tag17"
"Tag": "tag17"
},
"18": {
"tag": "tag18"
"Tag": "tag18"
}
},
"pmapp": {
"PMapP": {
"19": {
"tag": "tag19"
"Tag": "tag19"
},
"20": null
},
"emptymap": null,
"nilmap": null,
"slice": [],
"slicep": [],
"pslice": [
"EmptyMap": null,
"NilMap": null,
"Slice": [],
"SliceP": [],
"PSlice": [
{
"tag": "tag20"
"Tag": "tag20"
},
{
"tag": "tag21"
"Tag": "tag21"
}
],
"pslicep": [
"PSliceP": [
{
"tag": "tag22"
"Tag": "tag22"
},
null,
{
"tag": "tag23"
"Tag": "tag23"
}
],
"emptyslice": [],
"nilslice": [],
"stringslice": [],
"byteslice": [],
"small": {
"tag": ""
"EmptySlice": [],
"NilSlice": [],
"StringSlice": [],
"ByteSlice": [],
"Small": {
"Tag": ""
},
"psmall": null,
"ppsmall": {
"tag": "tag31"
"PSmall": null,
"PPSmall": {
"Tag": "tag31"
},
"interface": null,
"pinterface": 5.2
"Interface": null,
"PInterface": 5.2
}`
var pallValueCompact = strings.Map(noSpace, pallValueIndent)
......@@ -11,7 +11,6 @@ import (
"runtime"
"sort"
"strconv"
"strings"
)
// 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
// 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.
//
// Interface values encode as the value contained in the interface.
......@@ -202,7 +201,7 @@ func (e *encodeState) reflectValue(v reflect.Value) {
if f.Tag != "" {
e.string(f.Tag)
} else {
e.string(strings.ToLower(f.Name))
e.string(f.Name)
}
e.WriteByte(':')
e.reflectValue(v.Field(i))
......
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