Skip to content

Utilize MarshalJSON and UnmarshalJSON interface implementations #503

@klokare

Description

@klokare

Is your feature request related to a problem? Please describe.
Custom types can sometimes produce empty values in a RethinkDB document. I have implemented my own decimal type:

type Decimal struct {
	flags uint32
	high  uint32
	low   uint32
	mid   uint32
}

// MarshalJSON returns the decimal as a text string without quotes
func (d Decimal) MarshalJSON() ([]byte, error) { return d.MarshalText() }

// MarshalText encodes the receiver into UTF-8-encoded text and returns the result.
func (d Decimal) MarshalText() (text []byte, err error) {
	text = []byte(d.String())
	return text, nil
}

// UnmarshalJSON unmarshals the JSON value, ignoring quotes
func (d *Decimal) UnmarshalJSON(text []byte) error {
	return d.UnmarshalText(text)

}

// UnmarshalText unmarshals the decimal from the provided text.
func (d *Decimal) UnmarshalText(text []byte) (err error) {
	*d, err = Parse(string(text))
	return err
}

It implements both json.Marshaler and json.Unmarshaler. This type encodes and decodes without issue using the standard encoding/json package. So I was surprised to see the following document stored in RethinkDB

{
"candle": {
"bar_seqno": 12024547 ,
"close_price": { } ,
"high_price": { } ,
"low_price": { } ,
"open_price": { } ,
}
....
}

when using

type Candle struct {
  BarSeqno int `json:"bar_seqno"`
  OpenPrice Decimal `json:"open_price"`
  HighPrice Decimal `json:"high_price"`
  LowPrice Decimal `json:"low_price"`
  ClosePrice Decimal `json:"close_price"`
}

candle := Candle{
  BarSeqno: 12024547,
  OpenPrice: decimal.NewFromString("1.33028"),
  HighPrice: decimal.NewFromString("1.33028"),
  LowPrice: decimal.NewFromString("1.33028"),
  ClosePrice: decimal.NewFromString("1.33028"),
}

err := r.Table("candles").Insert(candle).Exec(session)

What I would expect to see is

{
"candle": {
"bar_seqno": 12024547 ,
"close_price": 1.33028 ,
"high_price": 1.33028 ,
"low_price": 1.33028 ,
"open_price": 1.33028 ,
}
....
}

Describe the solution you'd like
If this library could use the json.Marshaler and json.Unmarshaler implementations, I would get the expected value by just using

err := r.Table("candles").Insert(candle).Exec(session)

Describe alternatives you've considered
My workaround comes from this issue and is basically:

candles = []Candle{candle1, candle2, candle3}
b := new(bytes.Buffer)
	for _, candle := range candles {
		if err = json.NewEncoder(b).Encode(candle); err != nil {
			return err
		}
		if err = r.Table(name).Insert(r.JSON(b.String())).Exec(session); err != nil {
			return err
		}
		b.Reset()
	}

This is not only more verbose but also creates separate calls to Insert instead of sending a batch which impacts performance and also eliminates the transaction-like quality of sending a slice of objects to Insert.

Additional context
Is there something about RethinkDB or this library that would prevent adding this functionality? I would be happy to give it a try but not if someone has already proven it is a bad idea.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions