Skip to content

Commit

Permalink
Adding JSON marshal to custom types (#899)
Browse files Browse the repository at this point in the history
  • Loading branch information
Tang8330 authored Sep 9, 2024
1 parent c7d128a commit a97697e
Show file tree
Hide file tree
Showing 6 changed files with 112 additions and 22 deletions.
6 changes: 6 additions & 0 deletions lib/typing/decimal/decimal.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
package decimal

import (
"encoding/json"

"github.com/cockroachdb/apd/v3"
)

Expand All @@ -12,6 +14,10 @@ type Decimal struct {
value *apd.Decimal
}

func (d Decimal) MarshalJSON() ([]byte, error) {
return json.Marshal(d.String())
}

func NewDecimalWithPrecision(value *apd.Decimal, precision int32) *Decimal {
return &Decimal{
precision: precision,
Expand Down
25 changes: 25 additions & 0 deletions lib/typing/decimal/decimal_test.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package decimal

import (
"encoding/json"
"testing"

"github.com/artie-labs/transfer/lib/numbers"
Expand Down Expand Up @@ -39,3 +40,27 @@ func TestDecimal_Details(t *testing.T) {
assert.Equal(t, Details{scale: 2, precision: 10}, NewDecimalWithPrecision(numbers.MustParseDecimal("12345.12"), 10).Details())
assert.Equal(t, Details{scale: 3, precision: 10}, NewDecimalWithPrecision(numbers.MustParseDecimal("-12345.123"), 10).Details())
}

func TestMarshalJSON(t *testing.T) {
{
// Zero
bytes, err := NewDecimal(numbers.MustParseDecimal("0")).MarshalJSON()
assert.NoError(t, err)
assert.Equal(t, `"0"`, string(bytes))
}
{
// As a nested object
type Object struct {
Decimal *Decimal `json:"decimal"`
Foo string `json:"foo"`
}

var obj Object
obj.Decimal = NewDecimal(numbers.MustParseDecimal("0"))
obj.Foo = "bar"

bytes, err := json.Marshal(obj)
assert.NoError(t, err)
assert.Equal(t, `{"decimal":"0","foo":"bar"}`, string(bytes))
}
}
9 changes: 8 additions & 1 deletion lib/typing/ext/time.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
package ext

import "time"
import (
"encoding/json"
"time"
)

type ExtendedTimeKindType string

Expand Down Expand Up @@ -39,6 +42,10 @@ type ExtendedTime struct {
nestedKind NestedKind
}

func (e ExtendedTime) MarshalJSON() ([]byte, error) {
return json.Marshal(e.String(""))
}

func NewExtendedTime(t time.Time, kindType ExtendedTimeKindType, originalFormat string) *ExtendedTime {
if originalFormat == "" {
switch kindType {
Expand Down
35 changes: 35 additions & 0 deletions lib/typing/ext/time_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
package ext

import (
"encoding/json"
"testing"
"time"

"github.com/stretchr/testify/assert"
)

func TestExtendedTime_MarshalJSON(t *testing.T) {
extTime := NewExtendedTime(time.Date(2025, time.September, 13, 0, 0, 0, 123456000, time.UTC), DateTimeKindType, RFC3339Millisecond)

{
// Single value
bytes, err := json.Marshal(extTime)
assert.NoError(t, err)
assert.Equal(t, `"2025-09-13T00:00:00.123Z"`, string(bytes))
}
{
// As a nested object
type Object struct {
ExtendedTime *ExtendedTime `json:"extendedTime"`
Foo string `json:"foo"`
}

var obj Object
obj.ExtendedTime = extTime
obj.Foo = "bar"

bytes, err := json.Marshal(obj)
assert.NoError(t, err)
assert.Equal(t, `{"extendedTime":"2025-09-13T00:00:00.123Z","foo":"bar"}`, string(bytes))
}
}
4 changes: 2 additions & 2 deletions lib/typing/values/string.go
Original file line number Diff line number Diff line change
Expand Up @@ -92,15 +92,15 @@ func ToString(colVal any, colKind typing.KindDetails) (string, error) {
switch castedColVal := colVal.(type) {
// It's okay if it's not a *decimal.Decimal, so long as it's a float or string.
// By having the flexibility of handling both *decimal.Decimal and float64/float32/string values within the same batch will increase our ability for data digestion.
case float64, float32:
case int64, int32, float64, float32:
return fmt.Sprint(castedColVal), nil
case string:
return castedColVal, nil
case *decimal.Decimal:
return castedColVal.String(), nil
}

return "", fmt.Errorf("colVal is not *decimal.Decimal type, type is: %T", colVal)
return "", fmt.Errorf("unexpected colVal type: %T", colVal)
}

return fmt.Sprint(colVal), nil
Expand Down
55 changes: 36 additions & 19 deletions lib/typing/values/string_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -107,24 +107,41 @@ func TestToString(t *testing.T) {
}
{
// Extended Decimal
// Floats
val, err := ToString(float32(123.45), typing.EDecimal)
assert.NoError(t, err)
assert.Equal(t, "123.45", val)

val, err = ToString(123.45, typing.EDecimal)
assert.NoError(t, err)
assert.Equal(t, "123.45", val)

// String
val, err = ToString("123.45", typing.EDecimal)
assert.NoError(t, err)
assert.Equal(t, "123.45", val)

// Decimals
value := decimal.NewDecimalWithPrecision(numbers.MustParseDecimal("585692791691858.25"), 38)
val, err = ToString(value, typing.EDecimal)
assert.NoError(t, err)
assert.Equal(t, "585692791691858.25", val)
{
// Float32
val, err := ToString(float32(123.45), typing.EDecimal)
assert.NoError(t, err)
assert.Equal(t, "123.45", val)
}
{
// Float64
val, err := ToString(123.45, typing.EDecimal)
assert.NoError(t, err)
assert.Equal(t, "123.45", val)
}
{
// String
val, err := ToString("123.45", typing.EDecimal)
assert.NoError(t, err)
assert.Equal(t, "123.45", val)
}
{
// Decimal
val, err := ToString(decimal.NewDecimalWithPrecision(numbers.MustParseDecimal("585692791691858.25"), 38), typing.EDecimal)
assert.NoError(t, err)
assert.Equal(t, "585692791691858.25", val)
}
{
// Int32
val, err := ToString(int32(123), typing.EDecimal)
assert.NoError(t, err)
assert.Equal(t, "123", val)
}
{
// Int64
val, err := ToString(int64(123), typing.EDecimal)
assert.NoError(t, err)
assert.Equal(t, "123", val)
}
}
}

0 comments on commit a97697e

Please sign in to comment.