encoder.go 6.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199
  1. // Copyright (C) MongoDB, Inc. 2017-present.
  2. //
  3. // Licensed under the Apache License, Version 2.0 (the "License"); you may
  4. // not use this file except in compliance with the License. You may obtain
  5. // a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
  6. package bson
  7. import (
  8. "errors"
  9. "reflect"
  10. "sync"
  11. "go.mongodb.org/mongo-driver/bson/bsoncodec"
  12. "go.mongodb.org/mongo-driver/bson/bsonrw"
  13. )
  14. // This pool is used to keep the allocations of Encoders down. This is only used for the Marshal*
  15. // methods and is not consumable from outside of this package. The Encoders retrieved from this pool
  16. // must have both Reset and SetRegistry called on them.
  17. var encPool = sync.Pool{
  18. New: func() interface{} {
  19. return new(Encoder)
  20. },
  21. }
  22. // An Encoder writes a serialization format to an output stream. It writes to a bsonrw.ValueWriter
  23. // as the destination of BSON data.
  24. type Encoder struct {
  25. ec bsoncodec.EncodeContext
  26. vw bsonrw.ValueWriter
  27. errorOnInlineDuplicates bool
  28. intMinSize bool
  29. stringifyMapKeysWithFmt bool
  30. nilMapAsEmpty bool
  31. nilSliceAsEmpty bool
  32. nilByteSliceAsEmpty bool
  33. omitZeroStruct bool
  34. useJSONStructTags bool
  35. }
  36. // NewEncoder returns a new encoder that uses the DefaultRegistry to write to vw.
  37. func NewEncoder(vw bsonrw.ValueWriter) (*Encoder, error) {
  38. // TODO:(GODRIVER-2719): Remove error return value.
  39. if vw == nil {
  40. return nil, errors.New("cannot create a new Encoder with a nil ValueWriter")
  41. }
  42. return &Encoder{
  43. ec: bsoncodec.EncodeContext{Registry: DefaultRegistry},
  44. vw: vw,
  45. }, nil
  46. }
  47. // NewEncoderWithContext returns a new encoder that uses EncodeContext ec to write to vw.
  48. //
  49. // Deprecated: Use [NewEncoder] and use the Encoder configuration methods to set the desired marshal
  50. // behavior instead.
  51. func NewEncoderWithContext(ec bsoncodec.EncodeContext, vw bsonrw.ValueWriter) (*Encoder, error) {
  52. if ec.Registry == nil {
  53. ec = bsoncodec.EncodeContext{Registry: DefaultRegistry}
  54. }
  55. if vw == nil {
  56. return nil, errors.New("cannot create a new Encoder with a nil ValueWriter")
  57. }
  58. return &Encoder{
  59. ec: ec,
  60. vw: vw,
  61. }, nil
  62. }
  63. // Encode writes the BSON encoding of val to the stream.
  64. //
  65. // See [Marshal] for details about BSON marshaling behavior.
  66. func (e *Encoder) Encode(val interface{}) error {
  67. if marshaler, ok := val.(Marshaler); ok {
  68. // TODO(skriptble): Should we have a MarshalAppender interface so that we can have []byte reuse?
  69. buf, err := marshaler.MarshalBSON()
  70. if err != nil {
  71. return err
  72. }
  73. return bsonrw.Copier{}.CopyDocumentFromBytes(e.vw, buf)
  74. }
  75. encoder, err := e.ec.LookupEncoder(reflect.TypeOf(val))
  76. if err != nil {
  77. return err
  78. }
  79. // Copy the configurations applied to the Encoder over to the EncodeContext, which actually
  80. // communicates those configurations to the default ValueEncoders.
  81. if e.errorOnInlineDuplicates {
  82. e.ec.ErrorOnInlineDuplicates()
  83. }
  84. if e.intMinSize {
  85. e.ec.MinSize = true
  86. }
  87. if e.stringifyMapKeysWithFmt {
  88. e.ec.StringifyMapKeysWithFmt()
  89. }
  90. if e.nilMapAsEmpty {
  91. e.ec.NilMapAsEmpty()
  92. }
  93. if e.nilSliceAsEmpty {
  94. e.ec.NilSliceAsEmpty()
  95. }
  96. if e.nilByteSliceAsEmpty {
  97. e.ec.NilByteSliceAsEmpty()
  98. }
  99. if e.omitZeroStruct {
  100. e.ec.OmitZeroStruct()
  101. }
  102. if e.useJSONStructTags {
  103. e.ec.UseJSONStructTags()
  104. }
  105. return encoder.EncodeValue(e.ec, e.vw, reflect.ValueOf(val))
  106. }
  107. // Reset will reset the state of the Encoder, using the same *EncodeContext used in
  108. // the original construction but using vw.
  109. func (e *Encoder) Reset(vw bsonrw.ValueWriter) error {
  110. // TODO:(GODRIVER-2719): Remove error return value.
  111. e.vw = vw
  112. return nil
  113. }
  114. // SetRegistry replaces the current registry of the Encoder with r.
  115. func (e *Encoder) SetRegistry(r *bsoncodec.Registry) error {
  116. // TODO:(GODRIVER-2719): Remove error return value.
  117. e.ec.Registry = r
  118. return nil
  119. }
  120. // SetContext replaces the current EncodeContext of the encoder with ec.
  121. //
  122. // Deprecated: Use the Encoder configuration methods set the desired marshal behavior instead.
  123. func (e *Encoder) SetContext(ec bsoncodec.EncodeContext) error {
  124. // TODO:(GODRIVER-2719): Remove error return value.
  125. e.ec = ec
  126. return nil
  127. }
  128. // ErrorOnInlineDuplicates causes the Encoder to return an error if there is a duplicate field in
  129. // the marshaled BSON when the "inline" struct tag option is set.
  130. func (e *Encoder) ErrorOnInlineDuplicates() {
  131. e.errorOnInlineDuplicates = true
  132. }
  133. // IntMinSize causes the Encoder to marshal Go integer values (int, int8, int16, int32, int64, uint,
  134. // uint8, uint16, uint32, or uint64) as the minimum BSON int size (either 32 or 64 bits) that can
  135. // represent the integer value.
  136. func (e *Encoder) IntMinSize() {
  137. e.intMinSize = true
  138. }
  139. // StringifyMapKeysWithFmt causes the Encoder to convert Go map keys to BSON document field name
  140. // strings using fmt.Sprint instead of the default string conversion logic.
  141. func (e *Encoder) StringifyMapKeysWithFmt() {
  142. e.stringifyMapKeysWithFmt = true
  143. }
  144. // NilMapAsEmpty causes the Encoder to marshal nil Go maps as empty BSON documents instead of BSON
  145. // null.
  146. func (e *Encoder) NilMapAsEmpty() {
  147. e.nilMapAsEmpty = true
  148. }
  149. // NilSliceAsEmpty causes the Encoder to marshal nil Go slices as empty BSON arrays instead of BSON
  150. // null.
  151. func (e *Encoder) NilSliceAsEmpty() {
  152. e.nilSliceAsEmpty = true
  153. }
  154. // NilByteSliceAsEmpty causes the Encoder to marshal nil Go byte slices as empty BSON binary values
  155. // instead of BSON null.
  156. func (e *Encoder) NilByteSliceAsEmpty() {
  157. e.nilByteSliceAsEmpty = true
  158. }
  159. // TODO(GODRIVER-2820): Update the description to remove the note about only examining exported
  160. // TODO struct fields once the logic is updated to also inspect private struct fields.
  161. // OmitZeroStruct causes the Encoder to consider the zero value for a struct (e.g. MyStruct{})
  162. // as empty and omit it from the marshaled BSON when the "omitempty" struct tag option is set.
  163. //
  164. // Note that the Encoder only examines exported struct fields when determining if a struct is the
  165. // zero value. It considers pointers to a zero struct value (e.g. &MyStruct{}) not empty.
  166. func (e *Encoder) OmitZeroStruct() {
  167. e.omitZeroStruct = true
  168. }
  169. // UseJSONStructTags causes the Encoder to fall back to using the "json" struct tag if a "bson"
  170. // struct tag is not specified.
  171. func (e *Encoder) UseJSONStructTags() {
  172. e.useJSONStructTags = true
  173. }