encode.go 4.3 KB

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