Rietveld Code Review Tool
Help | Bug tracker | Discussion group | Source code | Sign in
(1676)

Side by Side Diff: src/pkg/json/encode.go

Issue 4918051: code review 4918051: json: add struct tag option to wrap literals in strings (Closed)
Patch Set: diff -r 73c091a3fc96 https://go.googlecode.com/hg Created 12 years, 7 months ago
Left:
Right:
Use n/p to move between diff chunks; N/P to move between comments. Please Sign in to add in-line comments.
Jump to:
View unified diff | Download patch
« no previous file with comments | « src/pkg/json/decode_test.go ('k') | src/pkg/json/encode_test.go » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
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 package json 7 package json
8 8
9 import ( 9 import (
10 "bytes" 10 "bytes"
11 "encoding/base64" 11 "encoding/base64"
12 "os" 12 "os"
13 "reflect" 13 "reflect"
14 "runtime" 14 "runtime"
15 "sort" 15 "sort"
16 "strconv" 16 "strconv"
17 "strings"
18 "unicode" 17 "unicode"
19 "utf8" 18 "utf8"
20 ) 19 )
21 20
22 // Marshal returns the JSON encoding of v. 21 // Marshal returns the JSON encoding of v.
23 // 22 //
24 // Marshal traverses the value v recursively. 23 // Marshal traverses the value v recursively.
25 // If an encountered value implements the Marshaler interface, 24 // If an encountered value implements the Marshaler interface,
26 // Marshal calls its MarshalJSON method to produce JSON. 25 // Marshal calls its MarshalJSON method to produce JSON.
27 // 26 //
(...skipping 24 matching lines...) Expand all
52 // // Specifies that Field appears in JSON as key "myName" and 51 // // Specifies that Field appears in JSON as key "myName" and
53 // // the field is omitted from the object if its value is empty, 52 // // the field is omitted from the object if its value is empty,
54 // // as defined above. 53 // // as defined above.
55 // Field int `json:"myName,omitempty"` 54 // Field int `json:"myName,omitempty"`
56 // 55 //
57 // // Field appears in JSON as key "Field" (the default), but 56 // // Field appears in JSON as key "Field" (the default), but
58 // // the field is skipped if empty. 57 // // the field is skipped if empty.
59 // // Note the leading comma. 58 // // Note the leading comma.
60 // Field int `json:",omitempty"` 59 // Field int `json:",omitempty"`
61 // 60 //
61 // The "string" option signals that a field is stored as JSON inside a
62 // JSON-encoded string. This extra level of encoding is sometimes
63 // used when communicating with JavaScript programs:
64 //
65 // Int64String int64 `json:",string"`
66 //
62 // The key name will be used if it's a non-empty string consisting of 67 // The key name will be used if it's a non-empty string consisting of
63 // only Unicode letters, digits, dollar signs, hyphens, and underscores. 68 // only Unicode letters, digits, dollar signs, hyphens, and underscores.
64 // 69 //
65 // Map values encode as JSON objects. 70 // Map values encode as JSON objects.
66 // The map's key type must be string; the object keys are used directly 71 // The map's key type must be string; the object keys are used directly
67 // as map keys. 72 // as map keys.
68 // 73 //
69 // Pointer values encode as the value pointed to. 74 // Pointer values encode as the value pointed to.
70 // A nil pointer encodes as the null JSON object. 75 // A nil pointer encodes as the null JSON object.
71 // 76 //
(...skipping 142 matching lines...) Expand 10 before | Expand all | Expand 10 after
214 return v.Uint() == 0 219 return v.Uint() == 0
215 case reflect.Float32, reflect.Float64: 220 case reflect.Float32, reflect.Float64:
216 return v.Float() == 0 221 return v.Float() == 0
217 case reflect.Interface, reflect.Ptr: 222 case reflect.Interface, reflect.Ptr:
218 return v.IsNil() 223 return v.IsNil()
219 } 224 }
220 return false 225 return false
221 } 226 }
222 227
223 func (e *encodeState) reflectValue(v reflect.Value) { 228 func (e *encodeState) reflectValue(v reflect.Value) {
229 e.reflectValueQuoted(v, false)
230 }
231
232 // reflectValueQuoted writes the value in v to the output.
233 // If quoted is true, the serialization is wrapped in a JSON string.
234 func (e *encodeState) reflectValueQuoted(v reflect.Value, quoted bool) {
224 if !v.IsValid() { 235 if !v.IsValid() {
225 e.WriteString("null") 236 e.WriteString("null")
226 return 237 return
227 } 238 }
228 239
229 if j, ok := v.Interface().(Marshaler); ok { 240 if j, ok := v.Interface().(Marshaler); ok {
230 b, err := j.MarshalJSON() 241 b, err := j.MarshalJSON()
231 if err == nil { 242 if err == nil {
232 // copy JSON into buffer, checking validity. 243 // copy JSON into buffer, checking validity.
233 err = Compact(&e.Buffer, b) 244 err = Compact(&e.Buffer, b)
234 } 245 }
235 if err != nil { 246 if err != nil {
236 e.error(&MarshalerError{v.Type(), err}) 247 e.error(&MarshalerError{v.Type(), err})
237 } 248 }
238 return 249 return
239 } 250 }
240 251
252 writeString := (*encodeState).WriteString
253 if quoted {
254 writeString = (*encodeState).string
255 }
256
241 switch v.Kind() { 257 switch v.Kind() {
242 case reflect.Bool: 258 case reflect.Bool:
243 x := v.Bool() 259 x := v.Bool()
244 if x { 260 if x {
245 » » » e.WriteString("true") 261 » » » writeString(e, "true")
246 } else { 262 } else {
247 » » » e.WriteString("false") 263 » » » writeString(e, "false")
248 } 264 }
249 265
250 case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.In t64: 266 case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.In t64:
251 » » e.WriteString(strconv.Itoa64(v.Int())) 267 » » writeString(e, strconv.Itoa64(v.Int()))
252 268
253 case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflec t.Uint64, reflect.Uintptr: 269 case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflec t.Uint64, reflect.Uintptr:
254 » » e.WriteString(strconv.Uitoa64(v.Uint())) 270 » » writeString(e, strconv.Uitoa64(v.Uint()))
255 271
256 case reflect.Float32, reflect.Float64: 272 case reflect.Float32, reflect.Float64:
257 » » e.WriteString(strconv.FtoaN(v.Float(), 'g', -1, v.Type().Bits()) ) 273 » » writeString(e, strconv.FtoaN(v.Float(), 'g', -1, v.Type().Bits() ))
258 274
259 case reflect.String: 275 case reflect.String:
260 » » e.string(v.String()) 276 » » if quoted {
277 » » » sb, err := Marshal(v.String())
278 » » » if err != nil {
279 » » » » e.error(err)
280 » » » }
281 » » » e.string(string(sb))
282 » » } else {
283 » » » e.string(v.String())
284 » » }
261 285
262 case reflect.Struct: 286 case reflect.Struct:
263 e.WriteByte('{') 287 e.WriteByte('{')
264 t := v.Type() 288 t := v.Type()
265 n := v.NumField() 289 n := v.NumField()
266 first := true 290 first := true
267 for i := 0; i < n; i++ { 291 for i := 0; i < n; i++ {
268 f := t.Field(i) 292 f := t.Field(i)
269 if f.PkgPath != "" { 293 if f.PkgPath != "" {
270 continue 294 continue
271 } 295 }
272 » » » tag, omitEmpty := f.Name, false 296 » » » tag, omitEmpty, quoted := f.Name, false, false
273 if tv := f.Tag.Get("json"); tv != "" { 297 if tv := f.Tag.Get("json"); tv != "" {
274 » » » » ss := strings.SplitN(tv, ",", 2) 298 » » » » name, opts := parseTag(tv)
275 » » » » if isValidTag(ss[0]) { 299 » » » » if isValidTag(name) {
276 » » » » » tag = ss[0] 300 » » » » » tag = name
277 } 301 }
278 » » » » if len(ss) > 1 { 302 » » » » omitEmpty = opts.Contains("omitempty")
279 » » » » » // Currently the only option is omitempt y, 303 » » » » quoted = opts.Contains("string")
280 » » » » » // so parsing is trivial.
281 » » » » » omitEmpty = ss[1] == "omitempty"
282 » » » » }
283 } 304 }
284 fieldValue := v.Field(i) 305 fieldValue := v.Field(i)
285 if omitEmpty && isEmptyValue(fieldValue) { 306 if omitEmpty && isEmptyValue(fieldValue) {
286 continue 307 continue
287 } 308 }
288 if first { 309 if first {
289 first = false 310 first = false
290 } else { 311 } else {
291 e.WriteByte(',') 312 e.WriteByte(',')
292 } 313 }
293 e.string(tag) 314 e.string(tag)
294 e.WriteByte(':') 315 e.WriteByte(':')
295 » » » e.reflectValue(fieldValue) 316 » » » e.reflectValueQuoted(fieldValue, quoted)
296 } 317 }
297 e.WriteByte('}') 318 e.WriteByte('}')
298 319
299 case reflect.Map: 320 case reflect.Map:
300 if v.Type().Key().Kind() != reflect.String { 321 if v.Type().Key().Kind() != reflect.String {
301 e.error(&UnsupportedTypeError{v.Type()}) 322 e.error(&UnsupportedTypeError{v.Type()})
302 } 323 }
303 if v.IsNil() { 324 if v.IsNil() {
304 e.WriteString("null") 325 e.WriteString("null")
305 break 326 break
(...skipping 67 matching lines...) Expand 10 before | Expand all | Expand 10 after
373 394
374 // stringValues is a slice of reflect.Value holding *reflect.StringValue. 395 // stringValues is a slice of reflect.Value holding *reflect.StringValue.
375 // It implements the methods to sort by string. 396 // It implements the methods to sort by string.
376 type stringValues []reflect.Value 397 type stringValues []reflect.Value
377 398
378 func (sv stringValues) Len() int { return len(sv) } 399 func (sv stringValues) Len() int { return len(sv) }
379 func (sv stringValues) Swap(i, j int) { sv[i], sv[j] = sv[j], sv[i] } 400 func (sv stringValues) Swap(i, j int) { sv[i], sv[j] = sv[j], sv[i] }
380 func (sv stringValues) Less(i, j int) bool { return sv.get(i) < sv.get(j) } 401 func (sv stringValues) Less(i, j int) bool { return sv.get(i) < sv.get(j) }
381 func (sv stringValues) get(i int) string { return sv[i].String() } 402 func (sv stringValues) get(i int) string { return sv[i].String() }
382 403
383 func (e *encodeState) string(s string) { 404 func (e *encodeState) string(s string) (int, os.Error) {
405 » len0 := e.Len()
384 e.WriteByte('"') 406 e.WriteByte('"')
385 start := 0 407 start := 0
386 for i := 0; i < len(s); { 408 for i := 0; i < len(s); {
387 if b := s[i]; b < utf8.RuneSelf { 409 if b := s[i]; b < utf8.RuneSelf {
388 if 0x20 <= b && b != '\\' && b != '"' && b != '<' && b ! = '>' { 410 if 0x20 <= b && b != '\\' && b != '"' && b != '<' && b ! = '>' {
389 i++ 411 i++
390 continue 412 continue
391 } 413 }
392 if start < i { 414 if start < i {
393 e.WriteString(s[start:i]) 415 e.WriteString(s[start:i])
(...skipping 24 matching lines...) Expand all
418 c, size := utf8.DecodeRuneInString(s[i:]) 440 c, size := utf8.DecodeRuneInString(s[i:])
419 if c == utf8.RuneError && size == 1 { 441 if c == utf8.RuneError && size == 1 {
420 e.error(&InvalidUTF8Error{s}) 442 e.error(&InvalidUTF8Error{s})
421 } 443 }
422 i += size 444 i += size
423 } 445 }
424 if start < len(s) { 446 if start < len(s) {
425 e.WriteString(s[start:]) 447 e.WriteString(s[start:])
426 } 448 }
427 e.WriteByte('"') 449 e.WriteByte('"')
450 return e.Len() - len0, nil
428 } 451 }
OLDNEW
« no previous file with comments | « src/pkg/json/decode_test.go ('k') | src/pkg/json/encode_test.go » ('j') | no next file with comments »

Powered by Google App Engine
RSS Feeds Recent Issues | This issue
This is Rietveld f62528b