]> Cypherpunks repositories - gostls13.git/commitdiff
json: preserve field name case by default
authorRuss Cox <rsc@golang.org>
Tue, 27 Apr 2010 17:24:00 +0000 (10:24 -0700)
committerRuss Cox <rsc@golang.org>
Tue, 27 Apr 2010 17:24:00 +0000 (10:24 -0700)
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

src/pkg/json/decode.go
src/pkg/json/decode_test.go
src/pkg/json/encode.go

index d37f609233372df4109e52fa080b89d66db16221..e8da35aceff3ff2c319ab080678db6434e748d47 100644 (file)
@@ -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) })
+                               }
                        }
                }
 
index 288bb1b40405faf8fc6ddeb853291fb3947603cc..9e7d810cee7d7d19ea1e8d74f5bffce9d2046fb7 100644 (file)
@@ -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)
index 1de22086d27e6f9c6d7678d9ad94c818bda2d580..839657c361a95ac34594844b35e965eaed1b3a9d 100644 (file)
@@ -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))