Last active
April 20, 2022 18:58
-
-
Save mniak/4364d173e06bbbba5aaae8f24cf94571 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package utils | |
import "encoding/json" | |
type Optional[T any] struct { | |
explicit bool | |
hasValue bool | |
value T | |
} | |
func (o *Optional[T]) UnmarshalJSON(b []byte) error { | |
if len(b) == 0 { | |
return nil | |
} | |
o.explicit = true | |
if string(b) == "null" { | |
return nil | |
} | |
o.hasValue = true | |
return json.Unmarshal(b, &o.value) | |
} | |
func (o *Optional[T]) Explicit() bool { | |
return o.explicit | |
} | |
func (o *Optional[T]) Value() *T { | |
if !o.explicit || !o.hasValue { | |
return nil | |
} | |
return &o.value | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package utils | |
import ( | |
"encoding/json" | |
"testing" | |
"github.com/stretchr/testify/assert" | |
"github.com/stretchr/testify/require" | |
) | |
var ( | |
_ json.Unmarshaler = &Optional[int]{} | |
_ json.Unmarshaler = &Optional[string]{} | |
) | |
type WithOptionals struct { | |
IntField1 Optional[int] | |
IntField2 Optional[int] `json:"int_field_2"` | |
} | |
func TestOptional(t *testing.T) { | |
t.Run("Default should be implicit nulls", func(t *testing.T) { | |
var data WithOptionals | |
assert.False(t, data.IntField1.Explicit()) | |
assert.Nil(t, data.IntField1.Value()) | |
assert.False(t, data.IntField2.Explicit()) | |
assert.Nil(t, data.IntField2.Value()) | |
}) | |
} | |
func TestOptional_Unmarshal(t *testing.T) { | |
t.Run("Implicit null", func(t *testing.T) { | |
var data WithOptionals | |
const text = `{}` | |
err := json.Unmarshal([]byte(text), &data) | |
require.NoError(t, err) | |
assert.False(t, data.IntField1.Explicit()) | |
assert.Nil(t, data.IntField1.Value()) | |
assert.False(t, data.IntField2.Explicit()) | |
assert.Nil(t, data.IntField2.Value()) | |
}) | |
t.Run("Explicit nulls", func(t *testing.T) { | |
var data WithOptionals | |
const text = `{ | |
"IntField1": null, | |
"int_field_2": null | |
}` | |
err := json.Unmarshal([]byte(text), &data) | |
require.NoError(t, err) | |
assert.True(t, data.IntField1.Explicit()) | |
assert.Nil(t, data.IntField1.Value()) | |
assert.True(t, data.IntField2.Explicit()) | |
assert.Nil(t, data.IntField2.Value()) | |
}) | |
t.Run(`Explicit string "null" should fail`, func(t *testing.T) { | |
var data WithOptionals | |
const text = `{ | |
"IntField1": "null", | |
"int_field_2": "null" | |
}` | |
err := json.Unmarshal([]byte(text), &data) | |
require.Error(t, err) | |
}) | |
t.Run("Happy scenario", func(t *testing.T) { | |
var data WithOptionals | |
const text = `{ | |
"IntField1": 123, | |
"int_field_2": 456 | |
}` | |
err := json.Unmarshal([]byte(text), &data) | |
require.NoError(t, err) | |
assert.True(t, data.IntField1.Explicit()) | |
assert.Equal(t, 123, *data.IntField1.Value()) | |
assert.True(t, data.IntField2.Explicit()) | |
assert.Equal(t, 456, *data.IntField2.Value()) | |
}) | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment