Add JSON Marshal and Unmarshal (#68)
Support serializing and deserializing atomic objects to/from JSON using
the JSON representation of the underlying types. Without this,
marshaling returns `{}`.
Per discussion in #68, `atomic.Value` does not yet implement support
because there's an open question as to whether it should implement it
even if the underlying type doesn't support JSON marshaling/
unmarshaling.
Resolves #49
This commit is contained in:
@@ -8,8 +8,8 @@ env:
|
||||
|
||||
matrix:
|
||||
include:
|
||||
- go: 1.12.x
|
||||
- go: 1.13.x
|
||||
- go: oldstable
|
||||
- go: stable
|
||||
env: LINT=1
|
||||
|
||||
cache:
|
||||
|
||||
106
atomic.go
106
atomic.go
@@ -23,6 +23,7 @@
|
||||
package atomic
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"math"
|
||||
"sync/atomic"
|
||||
"time"
|
||||
@@ -76,6 +77,21 @@ func (i *Int32) Swap(n int32) int32 {
|
||||
return atomic.SwapInt32(&i.v, n)
|
||||
}
|
||||
|
||||
// MarshalJSON encodes the wrapped int32 into JSON.
|
||||
func (i *Int32) MarshalJSON() ([]byte, error) {
|
||||
return json.Marshal(i.Load())
|
||||
}
|
||||
|
||||
// UnmarshalJSON decodes JSON into the wrapped int32.
|
||||
func (i *Int32) UnmarshalJSON(b []byte) error {
|
||||
var v int32
|
||||
if err := json.Unmarshal(b, &v); err != nil {
|
||||
return err
|
||||
}
|
||||
i.Store(v)
|
||||
return nil
|
||||
}
|
||||
|
||||
// Int64 is an atomic wrapper around an int64.
|
||||
type Int64 struct{ v int64 }
|
||||
|
||||
@@ -124,6 +140,21 @@ func (i *Int64) Swap(n int64) int64 {
|
||||
return atomic.SwapInt64(&i.v, n)
|
||||
}
|
||||
|
||||
// MarshalJSON encodes the wrapped int64 into JSON.
|
||||
func (i *Int64) MarshalJSON() ([]byte, error) {
|
||||
return json.Marshal(i.Load())
|
||||
}
|
||||
|
||||
// UnmarshalJSON decodes JSON into the wrapped int64.
|
||||
func (i *Int64) UnmarshalJSON(b []byte) error {
|
||||
var v int64
|
||||
if err := json.Unmarshal(b, &v); err != nil {
|
||||
return err
|
||||
}
|
||||
i.Store(v)
|
||||
return nil
|
||||
}
|
||||
|
||||
// Uint32 is an atomic wrapper around an uint32.
|
||||
type Uint32 struct{ v uint32 }
|
||||
|
||||
@@ -172,6 +203,21 @@ func (i *Uint32) Swap(n uint32) uint32 {
|
||||
return atomic.SwapUint32(&i.v, n)
|
||||
}
|
||||
|
||||
// MarshalJSON encodes the wrapped uint32 into JSON.
|
||||
func (i *Uint32) MarshalJSON() ([]byte, error) {
|
||||
return json.Marshal(i.Load())
|
||||
}
|
||||
|
||||
// UnmarshalJSON decodes JSON into the wrapped uint32.
|
||||
func (i *Uint32) UnmarshalJSON(b []byte) error {
|
||||
var v uint32
|
||||
if err := json.Unmarshal(b, &v); err != nil {
|
||||
return err
|
||||
}
|
||||
i.Store(v)
|
||||
return nil
|
||||
}
|
||||
|
||||
// Uint64 is an atomic wrapper around a uint64.
|
||||
type Uint64 struct{ v uint64 }
|
||||
|
||||
@@ -220,6 +266,21 @@ func (i *Uint64) Swap(n uint64) uint64 {
|
||||
return atomic.SwapUint64(&i.v, n)
|
||||
}
|
||||
|
||||
// MarshalJSON encodes the wrapped uint64 into JSON.
|
||||
func (i *Uint64) MarshalJSON() ([]byte, error) {
|
||||
return json.Marshal(i.Load())
|
||||
}
|
||||
|
||||
// UnmarshalJSON decodes JSON into the wrapped uint64.
|
||||
func (i *Uint64) UnmarshalJSON(b []byte) error {
|
||||
var v uint64
|
||||
if err := json.Unmarshal(b, &v); err != nil {
|
||||
return err
|
||||
}
|
||||
i.Store(v)
|
||||
return nil
|
||||
}
|
||||
|
||||
// Bool is an atomic Boolean.
|
||||
type Bool struct{ v uint32 }
|
||||
|
||||
@@ -269,6 +330,21 @@ func boolToInt(b bool) uint32 {
|
||||
return 0
|
||||
}
|
||||
|
||||
// MarshalJSON encodes the wrapped bool into JSON.
|
||||
func (b *Bool) MarshalJSON() ([]byte, error) {
|
||||
return json.Marshal(b.Load())
|
||||
}
|
||||
|
||||
// UnmarshalJSON decodes JSON into the wrapped bool.
|
||||
func (b *Bool) UnmarshalJSON(t []byte) error {
|
||||
var v bool
|
||||
if err := json.Unmarshal(t, &v); err != nil {
|
||||
return err
|
||||
}
|
||||
b.Store(v)
|
||||
return nil
|
||||
}
|
||||
|
||||
// Float64 is an atomic wrapper around float64.
|
||||
type Float64 struct {
|
||||
v uint64
|
||||
@@ -310,6 +386,21 @@ func (f *Float64) CAS(old, new float64) bool {
|
||||
return atomic.CompareAndSwapUint64(&f.v, math.Float64bits(old), math.Float64bits(new))
|
||||
}
|
||||
|
||||
// MarshalJSON encodes the wrapped float64 into JSON.
|
||||
func (f *Float64) MarshalJSON() ([]byte, error) {
|
||||
return json.Marshal(f.Load())
|
||||
}
|
||||
|
||||
// UnmarshalJSON decodes JSON into the wrapped float64.
|
||||
func (f *Float64) UnmarshalJSON(b []byte) error {
|
||||
var v float64
|
||||
if err := json.Unmarshal(b, &v); err != nil {
|
||||
return err
|
||||
}
|
||||
f.Store(v)
|
||||
return nil
|
||||
}
|
||||
|
||||
// Duration is an atomic wrapper around time.Duration
|
||||
// https://godoc.org/time#Duration
|
||||
type Duration struct {
|
||||
@@ -351,6 +442,21 @@ func (d *Duration) CAS(old, new time.Duration) bool {
|
||||
return d.v.CAS(int64(old), int64(new))
|
||||
}
|
||||
|
||||
// MarshalJSON encodes the wrapped time.Duration into JSON.
|
||||
func (d *Duration) MarshalJSON() ([]byte, error) {
|
||||
return json.Marshal(d.Load())
|
||||
}
|
||||
|
||||
// UnmarshalJSON decodes JSON into the wrapped time.Duration.
|
||||
func (d *Duration) UnmarshalJSON(b []byte) error {
|
||||
var v time.Duration
|
||||
if err := json.Unmarshal(b, &v); err != nil {
|
||||
return err
|
||||
}
|
||||
d.Store(v)
|
||||
return nil
|
||||
}
|
||||
|
||||
// Value shadows the type of the same name from sync/atomic
|
||||
// https://godoc.org/sync/atomic#Value
|
||||
type Value struct{ atomic.Value }
|
||||
|
||||
@@ -21,6 +21,8 @@
|
||||
package atomic
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
@@ -45,6 +47,19 @@ func TestInt32(t *testing.T) {
|
||||
|
||||
atom.Store(42)
|
||||
require.Equal(t, int32(42), atom.Load(), "Store didn't set the correct value.")
|
||||
|
||||
bytes, err := json.Marshal(atom)
|
||||
require.NoError(t, err, "json.Marshal errored unexpectedly.")
|
||||
require.Equal(t, []byte("42"), bytes, "json.Marshal encoded the wrong bytes.")
|
||||
|
||||
err = json.Unmarshal([]byte("40"), &atom)
|
||||
require.NoError(t, err, "json.Unmarshal errored unexpectedly.")
|
||||
require.Equal(t, int32(40), atom.Load(), "json.Unmarshal didn't set the correct value.")
|
||||
|
||||
err = json.Unmarshal([]byte("\"40\""), &atom)
|
||||
require.Error(t, err, "json.Unmarshal didn't error as expected.")
|
||||
require.True(t, errors.As(err, new(*json.UnmarshalTypeError)),
|
||||
"json.Unmarshal failed with unexpected error %v, want UnmarshalTypeError.", err)
|
||||
}
|
||||
|
||||
func TestInt64(t *testing.T) {
|
||||
@@ -64,6 +79,19 @@ func TestInt64(t *testing.T) {
|
||||
|
||||
atom.Store(42)
|
||||
require.Equal(t, int64(42), atom.Load(), "Store didn't set the correct value.")
|
||||
|
||||
bytes, err := json.Marshal(atom)
|
||||
require.NoError(t, err, "json.Marshal errored unexpectedly.")
|
||||
require.Equal(t, []byte("42"), bytes, "json.Marshal encoded the wrong bytes.")
|
||||
|
||||
err = json.Unmarshal([]byte("40"), &atom)
|
||||
require.NoError(t, err, "json.Unmarshal errored unexpectedly.")
|
||||
require.Equal(t, int64(40), atom.Load(), "json.Unmarshal didn't set the correct value.")
|
||||
|
||||
err = json.Unmarshal([]byte("\"40\""), &atom)
|
||||
require.Error(t, err, "json.Unmarshal didn't error as expected.")
|
||||
require.True(t, errors.As(err, new(*json.UnmarshalTypeError)),
|
||||
"json.Unmarshal failed with unexpected error %v, want UnmarshalTypeError.", err)
|
||||
}
|
||||
|
||||
func TestUint32(t *testing.T) {
|
||||
@@ -83,6 +111,19 @@ func TestUint32(t *testing.T) {
|
||||
|
||||
atom.Store(42)
|
||||
require.Equal(t, uint32(42), atom.Load(), "Store didn't set the correct value.")
|
||||
|
||||
bytes, err := json.Marshal(atom)
|
||||
require.NoError(t, err, "json.Marshal errored unexpectedly.")
|
||||
require.Equal(t, []byte("42"), bytes, "json.Marshal encoded the wrong bytes.")
|
||||
|
||||
err = json.Unmarshal([]byte("40"), &atom)
|
||||
require.NoError(t, err, "json.Unmarshal errored unexpectedly.")
|
||||
require.Equal(t, uint32(40), atom.Load(), "json.Unmarshal didn't set the correct value.")
|
||||
|
||||
err = json.Unmarshal([]byte("\"40\""), &atom)
|
||||
require.Error(t, err, "json.Unmarshal didn't error as expected.")
|
||||
require.True(t, errors.As(err, new(*json.UnmarshalTypeError)),
|
||||
"json.Unmarshal failed with unexpected error %v, want UnmarshalTypeError.", err)
|
||||
}
|
||||
|
||||
func TestUint64(t *testing.T) {
|
||||
@@ -102,6 +143,19 @@ func TestUint64(t *testing.T) {
|
||||
|
||||
atom.Store(42)
|
||||
require.Equal(t, uint64(42), atom.Load(), "Store didn't set the correct value.")
|
||||
|
||||
bytes, err := json.Marshal(atom)
|
||||
require.NoError(t, err, "json.Marshal errored unexpectedly.")
|
||||
require.Equal(t, []byte("42"), bytes, "json.Marshal encoded the wrong bytes.")
|
||||
|
||||
err = json.Unmarshal([]byte("40"), &atom)
|
||||
require.NoError(t, err, "json.Unmarshal errored unexpectedly.")
|
||||
require.Equal(t, uint64(40), atom.Load(), "json.Unmarshal didn't set the correct value.")
|
||||
|
||||
err = json.Unmarshal([]byte("\"40\""), &atom)
|
||||
require.Error(t, err, "json.Unmarshal didn't error as expected.")
|
||||
require.True(t, errors.As(err, new(*json.UnmarshalTypeError)),
|
||||
"json.Unmarshal failed with unexpected error %v, want UnmarshalTypeError.", err)
|
||||
}
|
||||
|
||||
func TestBool(t *testing.T) {
|
||||
@@ -126,6 +180,20 @@ func TestBool(t *testing.T) {
|
||||
|
||||
prev = atom.Swap(true)
|
||||
require.False(t, prev, "Expected Swap to return previous value.")
|
||||
|
||||
atom.Store(true)
|
||||
bytes, err := json.Marshal(atom)
|
||||
require.NoError(t, err, "json.Marshal errored unexpectedly.")
|
||||
require.Equal(t, []byte("true"), bytes, "json.Marshal encoded the wrong bytes.")
|
||||
|
||||
err = json.Unmarshal([]byte("false"), &atom)
|
||||
require.NoError(t, err, "json.Unmarshal errored unexpectedly.")
|
||||
require.False(t, atom.Load(), "json.Unmarshal didn't set the correct value.")
|
||||
|
||||
err = json.Unmarshal([]byte("42"), &atom)
|
||||
require.Error(t, err, "json.Unmarshal didn't error as expected.")
|
||||
require.True(t, errors.As(err, new(*json.UnmarshalTypeError)),
|
||||
"json.Unmarshal failed with unexpected error %v, want UnmarshalTypeError.", err)
|
||||
}
|
||||
|
||||
func TestFloat64(t *testing.T) {
|
||||
@@ -141,6 +209,20 @@ func TestFloat64(t *testing.T) {
|
||||
require.Equal(t, float64(42.0), atom.Load(), "Store didn't set the correct value.")
|
||||
require.Equal(t, float64(42.5), atom.Add(0.5), "Add didn't work.")
|
||||
require.Equal(t, float64(42.0), atom.Sub(0.5), "Sub didn't work.")
|
||||
|
||||
atom.Store(42.5)
|
||||
bytes, err := json.Marshal(atom)
|
||||
require.NoError(t, err, "json.Marshal errored unexpectedly.")
|
||||
require.Equal(t, []byte("42.5"), bytes, "json.Marshal encoded the wrong bytes.")
|
||||
|
||||
err = json.Unmarshal([]byte("40.5"), &atom)
|
||||
require.NoError(t, err, "json.Unmarshal errored unexpectedly.")
|
||||
require.Equal(t, float64(40.5), atom.Load(), "json.Unmarshal didn't set the correct value.")
|
||||
|
||||
err = json.Unmarshal([]byte("\"40.5\""), &atom)
|
||||
require.Error(t, err, "json.Unmarshal didn't error as expected.")
|
||||
require.True(t, errors.As(err, new(*json.UnmarshalTypeError)),
|
||||
"json.Unmarshal failed with unexpected error %v, want UnmarshalTypeError.", err)
|
||||
}
|
||||
|
||||
func TestDuration(t *testing.T) {
|
||||
@@ -158,6 +240,20 @@ func TestDuration(t *testing.T) {
|
||||
|
||||
atom.Store(10 * time.Minute)
|
||||
require.Equal(t, 10*time.Minute, atom.Load(), "Store didn't set the correct value.")
|
||||
|
||||
atom.Store(time.Second)
|
||||
bytes, err := json.Marshal(atom)
|
||||
require.NoError(t, err, "json.Marshal errored unexpectedly.")
|
||||
require.Equal(t, []byte("1000000000"), bytes, "json.Marshal encoded the wrong bytes.")
|
||||
|
||||
err = json.Unmarshal([]byte("1000000000"), &atom)
|
||||
require.NoError(t, err, "json.Unmarshal errored unexpectedly.")
|
||||
require.Equal(t, time.Second, atom.Load(), "json.Unmarshal didn't set the correct value.")
|
||||
|
||||
err = json.Unmarshal([]byte("\"1000000000\""), &atom)
|
||||
require.Error(t, err, "json.Unmarshal didn't error as expected.")
|
||||
require.True(t, errors.As(err, new(*json.UnmarshalTypeError)),
|
||||
"json.Unmarshal failed with unexpected error %v, want UnmarshalTypeError.", err)
|
||||
}
|
||||
|
||||
func TestValue(t *testing.T) {
|
||||
|
||||
19
string.go
19
string.go
@@ -20,6 +20,10 @@
|
||||
|
||||
package atomic
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
)
|
||||
|
||||
// String is an atomic type-safe wrapper around Value for strings.
|
||||
type String struct{ v Value }
|
||||
|
||||
@@ -41,6 +45,21 @@ func (s *String) Load() string {
|
||||
return v.(string)
|
||||
}
|
||||
|
||||
// MarshalJSON encodes the wrapped string into JSON.
|
||||
func (s *String) MarshalJSON() ([]byte, error) {
|
||||
return json.Marshal(s.Load())
|
||||
}
|
||||
|
||||
// UnmarshalJSON decodes JSON into the wrapped string.
|
||||
func (s *String) UnmarshalJSON(b []byte) error {
|
||||
var v string
|
||||
if err := json.Unmarshal(b, &v); err != nil {
|
||||
return err
|
||||
}
|
||||
s.Store(v)
|
||||
return nil
|
||||
}
|
||||
|
||||
// Store atomically stores the passed string.
|
||||
// Note: Converting the string to an interface{} to store in the Value
|
||||
// requires an allocation.
|
||||
|
||||
@@ -21,6 +21,8 @@
|
||||
package atomic
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/require"
|
||||
@@ -40,4 +42,17 @@ func TestString(t *testing.T) {
|
||||
|
||||
atom = NewString("bcd")
|
||||
require.Equal(t, "bcd", atom.Load(), "Expected Load to return initialized value")
|
||||
|
||||
bytes, err := json.Marshal(atom)
|
||||
require.NoError(t, err, "json.Marshal errored unexpectedly.")
|
||||
require.Equal(t, []byte("\"bcd\""), bytes, "json.Marshal encoded the wrong bytes.")
|
||||
|
||||
err = json.Unmarshal([]byte("\"abc\""), &atom)
|
||||
require.NoError(t, err, "json.Unmarshal errored unexpectedly.")
|
||||
require.Equal(t, "abc", atom.Load(), "json.Unmarshal didn't set the correct value.")
|
||||
|
||||
err = json.Unmarshal([]byte("42"), &atom)
|
||||
require.Error(t, err, "json.Unmarshal didn't error as expected.")
|
||||
require.True(t, errors.As(err, new(*json.UnmarshalTypeError)),
|
||||
"json.Unmarshal failed with unexpected error %v, want UnmarshalTypeError.", err)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user