123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199 |
- // Copyright (C) MongoDB, Inc. 2017-present.
- //
- // Licensed under the Apache License, Version 2.0 (the "License"); you may
- // not use this file except in compliance with the License. You may obtain
- // a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
- package bson
- import (
- "errors"
- "reflect"
- "sync"
- "go.mongodb.org/mongo-driver/bson/bsoncodec"
- "go.mongodb.org/mongo-driver/bson/bsonrw"
- )
- // This pool is used to keep the allocations of Encoders down. This is only used for the Marshal*
- // methods and is not consumable from outside of this package. The Encoders retrieved from this pool
- // must have both Reset and SetRegistry called on them.
- var encPool = sync.Pool{
- New: func() interface{} {
- return new(Encoder)
- },
- }
- // An Encoder writes a serialization format to an output stream. It writes to a bsonrw.ValueWriter
- // as the destination of BSON data.
- type Encoder struct {
- ec bsoncodec.EncodeContext
- vw bsonrw.ValueWriter
- errorOnInlineDuplicates bool
- intMinSize bool
- stringifyMapKeysWithFmt bool
- nilMapAsEmpty bool
- nilSliceAsEmpty bool
- nilByteSliceAsEmpty bool
- omitZeroStruct bool
- useJSONStructTags bool
- }
- // NewEncoder returns a new encoder that uses the DefaultRegistry to write to vw.
- func NewEncoder(vw bsonrw.ValueWriter) (*Encoder, error) {
- // TODO:(GODRIVER-2719): Remove error return value.
- if vw == nil {
- return nil, errors.New("cannot create a new Encoder with a nil ValueWriter")
- }
- return &Encoder{
- ec: bsoncodec.EncodeContext{Registry: DefaultRegistry},
- vw: vw,
- }, nil
- }
- // NewEncoderWithContext returns a new encoder that uses EncodeContext ec to write to vw.
- //
- // Deprecated: Use [NewEncoder] and use the Encoder configuration methods to set the desired marshal
- // behavior instead.
- func NewEncoderWithContext(ec bsoncodec.EncodeContext, vw bsonrw.ValueWriter) (*Encoder, error) {
- if ec.Registry == nil {
- ec = bsoncodec.EncodeContext{Registry: DefaultRegistry}
- }
- if vw == nil {
- return nil, errors.New("cannot create a new Encoder with a nil ValueWriter")
- }
- return &Encoder{
- ec: ec,
- vw: vw,
- }, nil
- }
- // Encode writes the BSON encoding of val to the stream.
- //
- // See [Marshal] for details about BSON marshaling behavior.
- func (e *Encoder) Encode(val interface{}) error {
- if marshaler, ok := val.(Marshaler); ok {
- // TODO(skriptble): Should we have a MarshalAppender interface so that we can have []byte reuse?
- buf, err := marshaler.MarshalBSON()
- if err != nil {
- return err
- }
- return bsonrw.Copier{}.CopyDocumentFromBytes(e.vw, buf)
- }
- encoder, err := e.ec.LookupEncoder(reflect.TypeOf(val))
- if err != nil {
- return err
- }
- // Copy the configurations applied to the Encoder over to the EncodeContext, which actually
- // communicates those configurations to the default ValueEncoders.
- if e.errorOnInlineDuplicates {
- e.ec.ErrorOnInlineDuplicates()
- }
- if e.intMinSize {
- e.ec.MinSize = true
- }
- if e.stringifyMapKeysWithFmt {
- e.ec.StringifyMapKeysWithFmt()
- }
- if e.nilMapAsEmpty {
- e.ec.NilMapAsEmpty()
- }
- if e.nilSliceAsEmpty {
- e.ec.NilSliceAsEmpty()
- }
- if e.nilByteSliceAsEmpty {
- e.ec.NilByteSliceAsEmpty()
- }
- if e.omitZeroStruct {
- e.ec.OmitZeroStruct()
- }
- if e.useJSONStructTags {
- e.ec.UseJSONStructTags()
- }
- return encoder.EncodeValue(e.ec, e.vw, reflect.ValueOf(val))
- }
- // Reset will reset the state of the Encoder, using the same *EncodeContext used in
- // the original construction but using vw.
- func (e *Encoder) Reset(vw bsonrw.ValueWriter) error {
- // TODO:(GODRIVER-2719): Remove error return value.
- e.vw = vw
- return nil
- }
- // SetRegistry replaces the current registry of the Encoder with r.
- func (e *Encoder) SetRegistry(r *bsoncodec.Registry) error {
- // TODO:(GODRIVER-2719): Remove error return value.
- e.ec.Registry = r
- return nil
- }
- // SetContext replaces the current EncodeContext of the encoder with ec.
- //
- // Deprecated: Use the Encoder configuration methods set the desired marshal behavior instead.
- func (e *Encoder) SetContext(ec bsoncodec.EncodeContext) error {
- // TODO:(GODRIVER-2719): Remove error return value.
- e.ec = ec
- return nil
- }
- // ErrorOnInlineDuplicates causes the Encoder to return an error if there is a duplicate field in
- // the marshaled BSON when the "inline" struct tag option is set.
- func (e *Encoder) ErrorOnInlineDuplicates() {
- e.errorOnInlineDuplicates = true
- }
- // IntMinSize causes the Encoder to marshal Go integer values (int, int8, int16, int32, int64, uint,
- // uint8, uint16, uint32, or uint64) as the minimum BSON int size (either 32 or 64 bits) that can
- // represent the integer value.
- func (e *Encoder) IntMinSize() {
- e.intMinSize = true
- }
- // StringifyMapKeysWithFmt causes the Encoder to convert Go map keys to BSON document field name
- // strings using fmt.Sprint instead of the default string conversion logic.
- func (e *Encoder) StringifyMapKeysWithFmt() {
- e.stringifyMapKeysWithFmt = true
- }
- // NilMapAsEmpty causes the Encoder to marshal nil Go maps as empty BSON documents instead of BSON
- // null.
- func (e *Encoder) NilMapAsEmpty() {
- e.nilMapAsEmpty = true
- }
- // NilSliceAsEmpty causes the Encoder to marshal nil Go slices as empty BSON arrays instead of BSON
- // null.
- func (e *Encoder) NilSliceAsEmpty() {
- e.nilSliceAsEmpty = true
- }
- // NilByteSliceAsEmpty causes the Encoder to marshal nil Go byte slices as empty BSON binary values
- // instead of BSON null.
- func (e *Encoder) NilByteSliceAsEmpty() {
- e.nilByteSliceAsEmpty = true
- }
- // TODO(GODRIVER-2820): Update the description to remove the note about only examining exported
- // TODO struct fields once the logic is updated to also inspect private struct fields.
- // OmitZeroStruct causes the Encoder to consider the zero value for a struct (e.g. MyStruct{})
- // as empty and omit it from the marshaled BSON when the "omitempty" struct tag option is set.
- //
- // Note that the Encoder only examines exported struct fields when determining if a struct is the
- // zero value. It considers pointers to a zero struct value (e.g. &MyStruct{}) not empty.
- func (e *Encoder) OmitZeroStruct() {
- e.omitZeroStruct = true
- }
- // UseJSONStructTags causes the Encoder to fall back to using the "json" struct tag if a "bson"
- // struct tag is not specified.
- func (e *Encoder) UseJSONStructTags() {
- e.useJSONStructTags = true
- }
|