encode.go 4.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175
  1. // Copyright 2018 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 json
  5. import (
  6. "strconv"
  7. "strings"
  8. "google.golang.org/protobuf/internal/detrand"
  9. "google.golang.org/protobuf/internal/errors"
  10. )
  11. // Encoder provides methods to write out JSON constructs and values. The user is
  12. // responsible for producing valid sequences of JSON constructs and values.
  13. type Encoder struct {
  14. indent string
  15. lastType Type
  16. indents []byte
  17. out []byte
  18. }
  19. // NewEncoder returns an Encoder.
  20. //
  21. // If indent is a non-empty string, it causes every entry for an Array or Object
  22. // to be preceded by the indent and trailed by a newline.
  23. func NewEncoder(indent string) (*Encoder, error) {
  24. e := &Encoder{}
  25. if len(indent) > 0 {
  26. if strings.Trim(indent, " \t") != "" {
  27. return nil, errors.New("indent may only be composed of space or tab characters")
  28. }
  29. e.indent = indent
  30. }
  31. return e, nil
  32. }
  33. // Bytes returns the content of the written bytes.
  34. func (e *Encoder) Bytes() []byte {
  35. return e.out
  36. }
  37. // WriteNull writes out the null value.
  38. func (e *Encoder) WriteNull() {
  39. e.prepareNext(Null)
  40. e.out = append(e.out, "null"...)
  41. }
  42. // WriteBool writes out the given boolean value.
  43. func (e *Encoder) WriteBool(b bool) {
  44. e.prepareNext(Bool)
  45. if b {
  46. e.out = append(e.out, "true"...)
  47. } else {
  48. e.out = append(e.out, "false"...)
  49. }
  50. }
  51. // WriteString writes out the given string in JSON string value.
  52. func (e *Encoder) WriteString(s string) error {
  53. e.prepareNext(String)
  54. var err error
  55. if e.out, err = appendString(e.out, s); err != nil {
  56. return err
  57. }
  58. return nil
  59. }
  60. // WriteFloat writes out the given float and bitSize in JSON number value.
  61. func (e *Encoder) WriteFloat(n float64, bitSize int) {
  62. e.prepareNext(Number)
  63. e.out = appendFloat(e.out, n, bitSize)
  64. }
  65. // WriteInt writes out the given signed integer in JSON number value.
  66. func (e *Encoder) WriteInt(n int64) {
  67. e.prepareNext(Number)
  68. e.out = append(e.out, strconv.FormatInt(n, 10)...)
  69. }
  70. // WriteUint writes out the given unsigned integer in JSON number value.
  71. func (e *Encoder) WriteUint(n uint64) {
  72. e.prepareNext(Number)
  73. e.out = append(e.out, strconv.FormatUint(n, 10)...)
  74. }
  75. // StartObject writes out the '{' symbol.
  76. func (e *Encoder) StartObject() {
  77. e.prepareNext(StartObject)
  78. e.out = append(e.out, '{')
  79. }
  80. // EndObject writes out the '}' symbol.
  81. func (e *Encoder) EndObject() {
  82. e.prepareNext(EndObject)
  83. e.out = append(e.out, '}')
  84. }
  85. // WriteName writes out the given string in JSON string value and the name
  86. // separator ':'.
  87. func (e *Encoder) WriteName(s string) error {
  88. e.prepareNext(Name)
  89. // Errors returned by appendString() are non-fatal.
  90. var err error
  91. e.out, err = appendString(e.out, s)
  92. e.out = append(e.out, ':')
  93. return err
  94. }
  95. // StartArray writes out the '[' symbol.
  96. func (e *Encoder) StartArray() {
  97. e.prepareNext(StartArray)
  98. e.out = append(e.out, '[')
  99. }
  100. // EndArray writes out the ']' symbol.
  101. func (e *Encoder) EndArray() {
  102. e.prepareNext(EndArray)
  103. e.out = append(e.out, ']')
  104. }
  105. // prepareNext adds possible comma and indentation for the next value based
  106. // on last type and indent option. It also updates lastType to next.
  107. func (e *Encoder) prepareNext(next Type) {
  108. defer func() {
  109. // Set lastType to next.
  110. e.lastType = next
  111. }()
  112. if len(e.indent) == 0 {
  113. // Need to add comma on the following condition.
  114. if e.lastType&(Null|Bool|Number|String|EndObject|EndArray) != 0 &&
  115. next&(Name|Null|Bool|Number|String|StartObject|StartArray) != 0 {
  116. e.out = append(e.out, ',')
  117. // For single-line output, add a random extra space after each
  118. // comma to make output unstable.
  119. if detrand.Bool() {
  120. e.out = append(e.out, ' ')
  121. }
  122. }
  123. return
  124. }
  125. switch {
  126. case e.lastType&(StartObject|StartArray) != 0:
  127. // If next type is NOT closing, add indent and newline.
  128. if next&(EndObject|EndArray) == 0 {
  129. e.indents = append(e.indents, e.indent...)
  130. e.out = append(e.out, '\n')
  131. e.out = append(e.out, e.indents...)
  132. }
  133. case e.lastType&(Null|Bool|Number|String|EndObject|EndArray) != 0:
  134. switch {
  135. // If next type is either a value or name, add comma and newline.
  136. case next&(Name|Null|Bool|Number|String|StartObject|StartArray) != 0:
  137. e.out = append(e.out, ',', '\n')
  138. // If next type is a closing object or array, adjust indentation.
  139. case next&(EndObject|EndArray) != 0:
  140. e.indents = e.indents[:len(e.indents)-len(e.indent)]
  141. e.out = append(e.out, '\n')
  142. }
  143. e.out = append(e.out, e.indents...)
  144. case e.lastType&Name != 0:
  145. e.out = append(e.out, ' ')
  146. // For multi-line output, add a random extra space after key: to make
  147. // output unstable.
  148. if detrand.Bool() {
  149. e.out = append(e.out, ' ')
  150. }
  151. }
  152. }