Left: | ||
Right: |
OLD | NEW |
---|---|
1 // Copyright 2010 The Go Authors. All rights reserved. | 1 // Copyright 2010 The Go Authors. All rights reserved. |
2 // Use of this source code is governed by a BSD-style | 2 // Use of this source code is governed by a BSD-style |
3 // license that can be found in the LICENSE file. | 3 // license that can be found in the LICENSE file. |
4 | 4 |
5 // Package json implements encoding and decoding of JSON objects as defined in | 5 // Package json implements encoding and decoding of JSON objects as defined in |
6 // RFC 4627. | 6 // RFC 4627. |
7 // | 7 // |
8 // See "JSON and Go" for an introduction to this package: | 8 // See "JSON and Go" for an introduction to this package: |
9 // http://blog.golang.org/2011/01/json-and-go.html | 9 // http://blog.golang.org/2011/01/json-and-go.html |
10 package json | 10 package json |
11 | 11 |
12 import ( | 12 import ( |
13 "bytes" | 13 "bytes" |
14 "encoding/base64" | 14 "encoding/base64" |
15 "reflect" | 15 "reflect" |
16 "runtime" | 16 "runtime" |
17 "sort" | 17 "sort" |
18 "strconv" | 18 "strconv" |
19 "sync" | |
19 "unicode" | 20 "unicode" |
20 "unicode/utf8" | 21 "unicode/utf8" |
21 ) | 22 ) |
22 | 23 |
23 // Marshal returns the JSON encoding of v. | 24 // Marshal returns the JSON encoding of v. |
24 // | 25 // |
25 // Marshal traverses the value v recursively. | 26 // Marshal traverses the value v recursively. |
26 // If an encountered value implements the Marshaler interface | 27 // If an encountered value implements the Marshaler interface |
27 // and is not a nil pointer, Marshal calls its MarshalJSON method | 28 // and is not a nil pointer, Marshal calls its MarshalJSON method |
28 // to produce JSON. The nil pointer exception is not strictly necessary | 29 // to produce JSON. The nil pointer exception is not strictly necessary |
(...skipping 160 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
189 type interfaceOrPtrValue interface { | 190 type interfaceOrPtrValue interface { |
190 IsNil() bool | 191 IsNil() bool |
191 Elem() reflect.Value | 192 Elem() reflect.Value |
192 } | 193 } |
193 | 194 |
194 var hex = "0123456789abcdef" | 195 var hex = "0123456789abcdef" |
195 | 196 |
196 // An encodeState encodes JSON into a bytes.Buffer. | 197 // An encodeState encodes JSON into a bytes.Buffer. |
197 type encodeState struct { | 198 type encodeState struct { |
198 bytes.Buffer // accumulated output | 199 bytes.Buffer // accumulated output |
200 | |
201 // tagCache is a cache of seen StructTag's "json" fields. | |
rsc
2011/11/21 15:06:51
Unused, I believe.
bradfitz
2011/11/21 15:25:54
Indeed.
| |
202 // the map starts out nil. | |
203 tagCache map[reflect.StructTag]string | |
199 } | 204 } |
200 | 205 |
201 func (e *encodeState) marshal(v interface{}) (err error) { | 206 func (e *encodeState) marshal(v interface{}) (err error) { |
202 defer func() { | 207 defer func() { |
203 if r := recover(); r != nil { | 208 if r := recover(); r != nil { |
204 if _, ok := r.(runtime.Error); ok { | 209 if _, ok := r.(runtime.Error); ok { |
205 panic(r) | 210 panic(r) |
206 } | 211 } |
207 err = r.(error) | 212 err = r.(error) |
208 } | 213 } |
(...skipping 79 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
288 if err != nil { | 293 if err != nil { |
289 e.error(err) | 294 e.error(err) |
290 } | 295 } |
291 e.string(string(sb)) | 296 e.string(string(sb)) |
292 } else { | 297 } else { |
293 e.string(v.String()) | 298 e.string(v.String()) |
294 } | 299 } |
295 | 300 |
296 case reflect.Struct: | 301 case reflect.Struct: |
297 e.WriteByte('{') | 302 e.WriteByte('{') |
298 t := v.Type() | |
299 n := v.NumField() | |
300 first := true | 303 first := true |
301 » » for i := 0; i < n; i++ { | 304 » » for _, ef := range encodeFields(v.Type()) { |
302 » » » f := t.Field(i) | 305 » » » fieldValue := v.Field(ef.i) |
303 » » » if f.PkgPath != "" { | 306 » » » if ef.omitEmpty && isEmptyValue(fieldValue) { |
304 » » » » continue | |
305 » » » } | |
306 » » » tag, omitEmpty, quoted := f.Name, false, false | |
307 » » » if tv := f.Tag.Get("json"); tv != "" { | |
308 » » » » if tv == "-" { | |
309 » » » » » continue | |
310 » » » » } | |
311 » » » » name, opts := parseTag(tv) | |
312 » » » » if isValidTag(name) { | |
313 » » » » » tag = name | |
314 » » » » } | |
315 » » » » omitEmpty = opts.Contains("omitempty") | |
316 » » » » quoted = opts.Contains("string") | |
317 » » » } | |
318 » » » fieldValue := v.Field(i) | |
319 » » » if omitEmpty && isEmptyValue(fieldValue) { | |
320 continue | 307 continue |
321 } | 308 } |
322 if first { | 309 if first { |
323 first = false | 310 first = false |
324 } else { | 311 } else { |
325 e.WriteByte(',') | 312 e.WriteByte(',') |
326 } | 313 } |
327 » » » e.string(tag) | 314 » » » e.string(ef.tag) |
328 e.WriteByte(':') | 315 e.WriteByte(':') |
329 » » » e.reflectValueQuoted(fieldValue, quoted) | 316 » » » e.reflectValueQuoted(fieldValue, ef.quoted) |
330 } | 317 } |
331 e.WriteByte('}') | 318 e.WriteByte('}') |
332 | 319 |
333 case reflect.Map: | 320 case reflect.Map: |
334 if v.Type().Key().Kind() != reflect.String { | 321 if v.Type().Key().Kind() != reflect.String { |
335 e.error(&UnsupportedTypeError{v.Type()}) | 322 e.error(&UnsupportedTypeError{v.Type()}) |
336 } | 323 } |
337 if v.IsNil() { | 324 if v.IsNil() { |
338 e.WriteString("null") | 325 e.WriteString("null") |
339 break | 326 break |
(...skipping 123 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
463 e.error(&InvalidUTF8Error{s}) | 450 e.error(&InvalidUTF8Error{s}) |
464 } | 451 } |
465 i += size | 452 i += size |
466 } | 453 } |
467 if start < len(s) { | 454 if start < len(s) { |
468 e.WriteString(s[start:]) | 455 e.WriteString(s[start:]) |
469 } | 456 } |
470 e.WriteByte('"') | 457 e.WriteByte('"') |
471 return e.Len() - len0, nil | 458 return e.Len() - len0, nil |
472 } | 459 } |
460 | |
461 // encodeField contains information about how to encode a field of a | |
462 // struct. | |
463 type encodeField struct { | |
464 i int // field index in struct | |
465 tag string | |
466 quoted bool | |
467 omitEmpty bool | |
468 } | |
469 | |
470 var ( | |
471 typeCacheLock sync.RWMutex | |
472 encodeFieldsCache = make(map[reflect.Type][]encodeField) | |
473 ) | |
474 | |
475 // encodeFields returns a slice of encodeField for a given | |
476 // struct type. | |
477 func encodeFields(t reflect.Type) []encodeField { | |
478 typeCacheLock.RLock() | |
479 defer typeCacheLock.RUnlock() | |
rsc
2011/11/21 15:06:51
The locking here is suspect. I think you need:
t
bradfitz
2011/11/21 15:25:54
uh, whoops.
| |
480 fs, ok := encodeFieldsCache[t] | |
481 if ok { | |
482 return fs | |
483 } | |
484 v := reflect.Zero(t) | |
485 n := v.NumField() | |
486 for i := 0; i < n; i++ { | |
487 f := t.Field(i) | |
488 if f.PkgPath != "" { | |
489 continue | |
490 } | |
491 var ef encodeField | |
492 ef.i = i | |
493 ef.tag = f.Name | |
494 | |
495 tv := f.Tag.Get("json") | |
496 if tv != "" { | |
497 if tv == "-" { | |
498 continue | |
499 } | |
500 name, opts := parseTag(tv) | |
501 if isValidTag(name) { | |
502 ef.tag = name | |
503 } | |
504 ef.omitEmpty = opts.Contains("omitempty") | |
505 ef.quoted = opts.Contains("string") | |
506 } | |
507 fs = append(fs, ef) | |
508 } | |
509 encodeFieldsCache[t] = fs | |
510 return fs | |
511 } | |
OLD | NEW |