diff options
author | ian <ian@138bc75d-0d04-0410-961f-82ee72b054a4> | 2011-10-26 23:57:58 +0000 |
---|---|---|
committer | ian <ian@138bc75d-0d04-0410-961f-82ee72b054a4> | 2011-10-26 23:57:58 +0000 |
commit | fa5d125b5cfa5c935e46d27a2cbcd71ae37687ac (patch) | |
tree | 19d182df05ead7ff8ba7ee00a7d57555e1383fdf /libgo/go/json | |
parent | e3d46e67996cf20ca3a75fccbb5a0007bfa3f992 (diff) | |
download | ppe42-gcc-fa5d125b5cfa5c935e46d27a2cbcd71ae37687ac.tar.gz ppe42-gcc-fa5d125b5cfa5c935e46d27a2cbcd71ae37687ac.zip |
Update Go library to last weekly.
git-svn-id: svn+ssh://gcc.gnu.org/svn/gcc/trunk@180552 138bc75d-0d04-0410-961f-82ee72b054a4
Diffstat (limited to 'libgo/go/json')
-rw-r--r-- | libgo/go/json/decode.go | 71 | ||||
-rw-r--r-- | libgo/go/json/decode_test.go | 6 | ||||
-rw-r--r-- | libgo/go/json/encode.go | 25 | ||||
-rw-r--r-- | libgo/go/json/encode_test.go | 2 | ||||
-rw-r--r-- | libgo/go/json/indent.go | 1 | ||||
-rw-r--r-- | libgo/go/json/scanner_test.go | 35 | ||||
-rw-r--r-- | libgo/go/json/stream_test.go | 25 |
7 files changed, 119 insertions, 46 deletions
diff --git a/libgo/go/json/decode.go b/libgo/go/json/decode.go index b7129f9846a..31b15a400df 100644 --- a/libgo/go/json/decode.go +++ b/libgo/go/json/decode.go @@ -22,17 +22,20 @@ import ( // Unmarshal parses the JSON-encoded data and stores the result // in the value pointed to by v. // -// Unmarshal traverses the value v recursively. -// If an encountered value implements the Unmarshaler interface, -// Unmarshal calls its UnmarshalJSON method with a well-formed -// JSON encoding. -// -// Otherwise, Unmarshal uses the inverse of the encodings that +// Unmarshal uses the inverse of the encodings that // Marshal uses, allocating maps, slices, and pointers as necessary, // with the following additional rules: // -// To unmarshal a JSON value into a nil interface value, the -// type stored in the interface value is one of: +// To unmarshal JSON into a pointer, Unmarshal first handles the case of +// the JSON being the JSON literal null. In that case, Unmarshal sets +// the pointer to nil. Otherwise, Unmarshal unmarshals the JSON into +// the value pointed at by the pointer. If the pointer is nil, Unmarshal +// allocates a new value for it to point to. +// +// To unmarshal JSON into an interface value, Unmarshal unmarshals +// the JSON into the concrete value contained in the interface value. +// If the interface value is nil, that is, has no concrete value stored in it, +// Unmarshal stores one of these in the interface value: // // bool, for JSON booleans // float64, for JSON numbers @@ -250,8 +253,8 @@ func (d *decodeState) value(v reflect.Value) { // indirect walks down v allocating pointers as needed, // until it gets to a non-pointer. // if it encounters an Unmarshaler, indirect stops and returns that. -// if wantptr is true, indirect stops at the last pointer. -func (d *decodeState) indirect(v reflect.Value, wantptr bool) (Unmarshaler, reflect.Value) { +// if decodingNull is true, indirect stops at the last pointer so it can be set to nil. +func (d *decodeState) indirect(v reflect.Value, decodingNull bool) (Unmarshaler, reflect.Value) { // If v is a named type and is addressable, // start with its address, so that if the type has pointer methods, // we find them. @@ -277,7 +280,7 @@ func (d *decodeState) indirect(v reflect.Value, wantptr bool) (Unmarshaler, refl break } - if pv.Elem().Kind() != reflect.Ptr && wantptr && pv.CanSet() && !isUnmarshaler { + if pv.Elem().Kind() != reflect.Ptr && decodingNull && pv.CanSet() { return nil, pv } if pv.IsNil() { @@ -391,11 +394,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. // the first byte of the object ('{') has been read already. func (d *decodeState) object(v reflect.Value) { @@ -485,24 +483,31 @@ func (d *decodeState) object(v reflect.Value) { var f reflect.StructField var ok bool st := sv.Type() - // First try for field with that tag. - if isValidTag(key) { - for i := 0; i < sv.NumField(); i++ { - f = st.Field(i) - tagName, _ := parseTag(f.Tag.Get("json")) - if tagName == key { - ok = true - break - } + for i := 0; i < sv.NumField(); i++ { + sf := st.Field(i) + tag := sf.Tag.Get("json") + if tag == "-" { + // Pretend this field doesn't exist. + continue + } + // First, tag match + tagName, _ := parseTag(tag) + if tagName == key { + f = sf + ok = true + break // no better match possible + } + // Second, exact field name match + if sf.Name == key { + f = sf + ok = true + } + // Third, case-insensitive field name match, + // but only if a better match hasn't already been seen + if !ok && strings.EqualFold(sf.Name, key) { + f = sf + ok = true } - } - if !ok { - // Second, exact match. - f, ok = st.FieldByName(key) - } - if !ok { - // Third, case-insensitive match. - f, ok = st.FieldByNameFunc(func(s string) bool { return matchName(key, s) }) } // Extract value; name must be exported. diff --git a/libgo/go/json/decode_test.go b/libgo/go/json/decode_test.go index 5f6c3f5b8d0..2c7cbc4a290 100644 --- a/libgo/go/json/decode_test.go +++ b/libgo/go/json/decode_test.go @@ -15,6 +15,7 @@ import ( type T struct { X string Y int + Z int `json:"-"` } type tx struct { @@ -42,7 +43,7 @@ var ( um0, um1 unmarshaler // target2 of unmarshaling ump = &um1 umtrue = unmarshaler{true} - umslice = []unmarshaler{unmarshaler{true}} + umslice = []unmarshaler{{true}} umslicep = new([]unmarshaler) umstruct = ustruct{unmarshaler{true}} ) @@ -68,6 +69,9 @@ var unmarshalTests = []unmarshalTest{ {`{"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)}}, + // Z has a "-" tag. + {`{"Y": 1, "Z": 2}`, new(T), T{Y: 1}, nil}, + // syntax errors {`{"X": "foo", "Y"}`, nil, nil, &SyntaxError{"invalid character '}' after object key", 17}}, diff --git a/libgo/go/json/encode.go b/libgo/go/json/encode.go index 16be5e2af16..46abe4360ed 100644 --- a/libgo/go/json/encode.go +++ b/libgo/go/json/encode.go @@ -24,8 +24,11 @@ import ( // Marshal returns the JSON encoding of v. // // Marshal traverses the value v recursively. -// If an encountered value implements the Marshaler interface, -// Marshal calls its MarshalJSON method to produce JSON. +// If an encountered value implements the Marshaler interface +// and is not a nil pointer, Marshal calls its MarshalJSON method +// to produce JSON. The nil pointer exception is not strictly necessary +// but mimics a similar, necessary exception in the behavior of +// UnmarshalJSON. // // Otherwise, Marshal uses the following type-dependent default encodings: // @@ -40,18 +43,23 @@ import ( // []byte encodes as a base64-encoded string. // // Struct values encode as JSON objects. Each exported struct field -// becomes a member of the object unless the field is empty and its tag -// specifies the "omitempty" option. The empty values are false, 0, any +// becomes a member of the object unless +// - 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 // 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 // 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 is ignored by this package. +// Field int `json:"-"` +// +// // Field appears in JSON as key "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, // // as defined above. // Field int `json:"myName,omitempty"` @@ -240,7 +248,7 @@ func (e *encodeState) reflectValueQuoted(v reflect.Value, quoted bool) { return } - if j, ok := v.Interface().(Marshaler); ok { + if j, ok := v.Interface().(Marshaler); ok && (v.Kind() != reflect.Ptr || !v.IsNil()) { b, err := j.MarshalJSON() if err == nil { // copy JSON into buffer, checking validity. @@ -298,6 +306,9 @@ func (e *encodeState) reflectValueQuoted(v reflect.Value, quoted bool) { } tag, omitEmpty, quoted := f.Name, false, false if tv := f.Tag.Get("json"); tv != "" { + if tv == "-" { + continue + } name, opts := parseTag(tv) if isValidTag(name) { tag = name diff --git a/libgo/go/json/encode_test.go b/libgo/go/json/encode_test.go index 012e9f143b4..f85bb6216a2 100644 --- a/libgo/go/json/encode_test.go +++ b/libgo/go/json/encode_test.go @@ -13,6 +13,7 @@ import ( type Optionals struct { Sr string `json:"sr"` So string `json:"so,omitempty"` + Sw string `json:"-"` Ir int `json:"omitempty"` // actually named omitempty, not an option Io int `json:"io,omitempty"` @@ -33,6 +34,7 @@ var optionalsExpected = `{ func TestOmitEmpty(t *testing.T) { var o Optionals + o.Sw = "something" o.Mr = map[string]interface{}{} o.Mo = map[string]interface{}{} diff --git a/libgo/go/json/indent.go b/libgo/go/json/indent.go index 000da42f6fb..2a753037308 100644 --- a/libgo/go/json/indent.go +++ b/libgo/go/json/indent.go @@ -59,6 +59,7 @@ func Indent(dst *bytes.Buffer, src []byte, prefix, indent string) os.Error { needIndent := false depth := 0 for _, c := range src { + scan.bytes++ v := scan.step(&scan, int(c)) if v == scanSkipSpace { continue diff --git a/libgo/go/json/scanner_test.go b/libgo/go/json/scanner_test.go index 023e7c81ee4..4d73eac8aaa 100644 --- a/libgo/go/json/scanner_test.go +++ b/libgo/go/json/scanner_test.go @@ -7,7 +7,9 @@ package json import ( "bytes" "math" + "os" "rand" + "reflect" "testing" ) @@ -136,6 +138,29 @@ func TestIndentBig(t *testing.T) { } } +type indentErrorTest struct { + in string + err os.Error +} + +var indentErrorTests = []indentErrorTest{ + {`{"X": "foo", "Y"}`, &SyntaxError{"invalid character '}' after object key", 17}}, + {`{"X": "foo" "Y": "bar"}`, &SyntaxError{"invalid character '\"' after object key:value pair", 13}}, +} + +func TestIdentErrors(t *testing.T) { + for i, tt := range indentErrorTests { + slice := make([]uint8, 0) + buf := bytes.NewBuffer(slice) + if err := Indent(buf, []uint8(tt.in), "", ""); err != nil { + if !reflect.DeepEqual(err, tt.err) { + t.Errorf("#%d: Indent: %#v", i, err) + continue + } + } + } +} + func TestNextValueBig(t *testing.T) { initBig() var scan scanner @@ -150,7 +175,7 @@ func TestNextValueBig(t *testing.T) { t.Errorf("invalid rest: %d", len(rest)) } - item, rest, err = nextValue(append(jsonBig, []byte("HELLO WORLD")...), &scan) + item, rest, err = nextValue(append(jsonBig, "HELLO WORLD"...), &scan) if err != nil { t.Fatalf("nextValue extra: %s", err) } @@ -235,10 +260,10 @@ func genValue(n int) interface{} { } func genString(stddev float64) string { - n := int(math.Fabs(rand.NormFloat64()*stddev + stddev/2)) + n := int(math.Abs(rand.NormFloat64()*stddev + stddev/2)) c := make([]int, n) for i := range c { - f := math.Fabs(rand.NormFloat64()*64 + 32) + f := math.Abs(rand.NormFloat64()*64 + 32) if f > 0x10ffff { f = 0x10ffff } @@ -248,7 +273,7 @@ func genString(stddev float64) string { } func genArray(n int) []interface{} { - f := int(math.Fabs(rand.NormFloat64()) * math.Fmin(10, float64(n/2))) + f := int(math.Abs(rand.NormFloat64()) * math.Min(10, float64(n/2))) if f > n { f = n } @@ -263,7 +288,7 @@ func genArray(n int) []interface{} { } func genMap(n int) map[string]interface{} { - f := int(math.Fabs(rand.NormFloat64()) * math.Fmin(10, float64(n/2))) + f := int(math.Abs(rand.NormFloat64()) * math.Min(10, float64(n/2))) if f > n { f = n } diff --git a/libgo/go/json/stream_test.go b/libgo/go/json/stream_test.go index 6ddaed9fe8f..ce5a7e6d656 100644 --- a/libgo/go/json/stream_test.go +++ b/libgo/go/json/stream_test.go @@ -120,3 +120,28 @@ func TestRawMessage(t *testing.T) { t.Fatalf("Marshal: have %#q want %#q", b, msg) } } + +func TestNullRawMessage(t *testing.T) { + // TODO(rsc): Should not need the * in *RawMessage + var data struct { + X float64 + Id *RawMessage + Y float32 + } + data.Id = new(RawMessage) + const msg = `{"X":0.1,"Id":null,"Y":0.2}` + err := Unmarshal([]byte(msg), &data) + if err != nil { + t.Fatalf("Unmarshal: %v", err) + } + if data.Id != nil { + t.Fatalf("Raw mismatch: have non-nil, want nil") + } + b, err := Marshal(&data) + if err != nil { + t.Fatalf("Marshal: %v", err) + } + if string(b) != msg { + t.Fatalf("Marshal: have %#q want %#q", b, msg) + } +} |