encode.go 4.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174
  1. // Copyright 2010 The Go Authors. All rights reserved.
  2. // Use of this source code is governed by a BSD-style
  3. // license that can be found in the LICENSE file.
  4. // Package jsonx extends the encoding/json package to encode JSON
  5. // incrementally and without requiring reflection.
  6. package jsonx
  7. import (
  8. "bytes"
  9. "encoding/json"
  10. "math"
  11. "reflect"
  12. "strconv"
  13. "unicode/utf8"
  14. )
  15. var hex = "0123456789abcdef"
  16. // AppendString escapes s appends it to buf.
  17. func AppendString(buf *bytes.Buffer, s string) {
  18. buf.WriteByte('"')
  19. start := 0
  20. for i := 0; i < len(s); {
  21. if b := s[i]; b < utf8.RuneSelf {
  22. if 0x20 <= b && b != '\\' && b != '"' && b != '<' && b != '>' && b != '&' {
  23. i++
  24. continue
  25. }
  26. if start < i {
  27. buf.WriteString(s[start:i])
  28. }
  29. switch b {
  30. case '\\', '"':
  31. buf.WriteByte('\\')
  32. buf.WriteByte(b)
  33. case '\n':
  34. buf.WriteByte('\\')
  35. buf.WriteByte('n')
  36. case '\r':
  37. buf.WriteByte('\\')
  38. buf.WriteByte('r')
  39. case '\t':
  40. buf.WriteByte('\\')
  41. buf.WriteByte('t')
  42. default:
  43. // This encodes bytes < 0x20 except for \n and \r,
  44. // as well as <, > and &. The latter are escaped because they
  45. // can lead to security holes when user-controlled strings
  46. // are rendered into JSON and served to some browsers.
  47. buf.WriteString(`\u00`)
  48. buf.WriteByte(hex[b>>4])
  49. buf.WriteByte(hex[b&0xF])
  50. }
  51. i++
  52. start = i
  53. continue
  54. }
  55. c, size := utf8.DecodeRuneInString(s[i:])
  56. if c == utf8.RuneError && size == 1 {
  57. if start < i {
  58. buf.WriteString(s[start:i])
  59. }
  60. buf.WriteString(`\ufffd`)
  61. i += size
  62. start = i
  63. continue
  64. }
  65. // U+2028 is LINE SEPARATOR.
  66. // U+2029 is PARAGRAPH SEPARATOR.
  67. // They are both technically valid characters in JSON strings,
  68. // but don't work in JSONP, which has to be evaluated as JavaScript,
  69. // and can lead to security holes there. It is valid JSON to
  70. // escape them, so we do so unconditionally.
  71. // See http://timelessrepo.com/json-isnt-a-javascript-subset for discussion.
  72. if c == '\u2028' || c == '\u2029' {
  73. if start < i {
  74. buf.WriteString(s[start:i])
  75. }
  76. buf.WriteString(`\u202`)
  77. buf.WriteByte(hex[c&0xF])
  78. i += size
  79. start = i
  80. continue
  81. }
  82. i += size
  83. }
  84. if start < len(s) {
  85. buf.WriteString(s[start:])
  86. }
  87. buf.WriteByte('"')
  88. }
  89. // AppendStringArray appends an array of string literals to buf.
  90. func AppendStringArray(buf *bytes.Buffer, a ...string) {
  91. buf.WriteByte('[')
  92. for i, s := range a {
  93. if i > 0 {
  94. buf.WriteByte(',')
  95. }
  96. AppendString(buf, s)
  97. }
  98. buf.WriteByte(']')
  99. }
  100. // AppendFloat appends a numeric literal representing the value to buf.
  101. func AppendFloat(buf *bytes.Buffer, x float64) error {
  102. var scratch [64]byte
  103. if math.IsInf(x, 0) || math.IsNaN(x) {
  104. return &json.UnsupportedValueError{
  105. Value: reflect.ValueOf(x),
  106. Str: strconv.FormatFloat(x, 'g', -1, 64),
  107. }
  108. }
  109. buf.Write(strconv.AppendFloat(scratch[:0], x, 'g', -1, 64))
  110. return nil
  111. }
  112. // AppendFloatArray appends an array of numeric literals to buf.
  113. func AppendFloatArray(buf *bytes.Buffer, a ...float64) error {
  114. buf.WriteByte('[')
  115. for i, x := range a {
  116. if i > 0 {
  117. buf.WriteByte(',')
  118. }
  119. if err := AppendFloat(buf, x); err != nil {
  120. return err
  121. }
  122. }
  123. buf.WriteByte(']')
  124. return nil
  125. }
  126. // AppendInt appends a numeric literal representing the value to buf.
  127. func AppendInt(buf *bytes.Buffer, x int64) {
  128. var scratch [64]byte
  129. buf.Write(strconv.AppendInt(scratch[:0], x, 10))
  130. }
  131. // AppendIntArray appends an array of numeric literals to buf.
  132. func AppendIntArray(buf *bytes.Buffer, a ...int64) {
  133. var scratch [64]byte
  134. buf.WriteByte('[')
  135. for i, x := range a {
  136. if i > 0 {
  137. buf.WriteByte(',')
  138. }
  139. buf.Write(strconv.AppendInt(scratch[:0], x, 10))
  140. }
  141. buf.WriteByte(']')
  142. }
  143. // AppendUint appends a numeric literal representing the value to buf.
  144. func AppendUint(buf *bytes.Buffer, x uint64) {
  145. var scratch [64]byte
  146. buf.Write(strconv.AppendUint(scratch[:0], x, 10))
  147. }
  148. // AppendUintArray appends an array of numeric literals to buf.
  149. func AppendUintArray(buf *bytes.Buffer, a ...uint64) {
  150. var scratch [64]byte
  151. buf.WriteByte('[')
  152. for i, x := range a {
  153. if i > 0 {
  154. buf.WriteByte(',')
  155. }
  156. buf.Write(strconv.AppendUint(scratch[:0], x, 10))
  157. }
  158. buf.WriteByte(']')
  159. }