Add JSON marshaling support for Variable types

Introduced a NewVariable function for creating generics-based Variable types. Added MarshalJSON methods to support JSON encoding for various Nil types, ensuring proper handling of nil values. Updated tests and examples to verify the new marshaling functionality.
This commit is contained in:
Winni Neessen 2024-09-02 12:13:54 +02:00
parent 25913bbf9f
commit 05d806adff
Signed by: wneessen
GPG key ID: 385AC9889632126E
3 changed files with 284 additions and 1 deletions

View file

@ -87,5 +87,12 @@ func main() {
output += fmt.Sprintf("String is: %s", example.String.Value()) output += fmt.Sprintf("String is: %s", example.String.Value())
} }
fmt.Println(output) fmt.Println(output)
data, err := json.Marshal(&example)
if err != nil {
fmt.Printf("failed to marshal JSON: %s", err)
os.Exit(1)
}
fmt.Println(data)
} }
``` ```

View file

@ -36,6 +36,14 @@ func (v *Variable[T]) Reset() {
v.notNil = false v.notNil = false
} }
// NewVariable returns a new Variable of generic type
func NewVariable[T any](value T) Variable[T] {
return Variable[T]{
notNil: true,
value: value,
}
}
// NilBoolean is an boolean type that can be nil // NilBoolean is an boolean type that can be nil
type NilBoolean = Variable[bool] type NilBoolean = Variable[bool]
@ -72,7 +80,7 @@ type NilFloat64 = Variable[float64]
// NilString is a string type that can be nil // NilString is a string type that can be nil
type NilString = Variable[string] type NilString = Variable[string]
// UnmarshalJSON interprets the generic Nil types and sets the value and notnil of the type // UnmarshalJSON satisfies the json.Unmarshaler interface for generic Variable types
func (v *Variable[T]) UnmarshalJSON(data []byte) error { func (v *Variable[T]) UnmarshalJSON(data []byte) error {
if string(data) != "null" { if string(data) != "null" {
v.value = *new(T) v.value = *new(T)
@ -81,3 +89,11 @@ func (v *Variable[T]) UnmarshalJSON(data []byte) error {
} }
return nil return nil
} }
// MarshalJSON satisfies the json.Marshaler interface for generic Variable types
func (v *Variable[T]) MarshalJSON() ([]byte, error) {
if !v.notNil {
return json.Marshal(nil)
}
return json.Marshal(v.value)
}

View file

@ -56,6 +56,25 @@ func TestVariable_UnmarshalJSON_Boolean(t *testing.T) {
} }
} }
func TestVariable_MarshalJSON_Boolean(t *testing.T) {
type JSONType struct {
Value NilBoolean `json:"bool"`
NilValue NilBoolean `json:"nilvalue,omitempty"`
}
expected := `{"bool":false,"nilvalue":null}`
jt := &JSONType{
Value: NewVariable(false),
}
data, err := json.Marshal(&jt)
if err != nil {
t.Errorf("failed to marshal json with nil types: %s", err)
}
if !bytes.Equal(data, []byte(expected)) {
t.Errorf("expected json to be %q, got %q", expected, string(data))
}
}
func TestVariable_UnmarshalJSON_ByteSlice(t *testing.T) { func TestVariable_UnmarshalJSON_ByteSlice(t *testing.T) {
type JSONType struct { type JSONType struct {
Value NilByteSlice `json:"bytes"` Value NilByteSlice `json:"bytes"`
@ -83,6 +102,25 @@ func TestVariable_UnmarshalJSON_ByteSlice(t *testing.T) {
} }
} }
func TestVariable_MarshalJSON_ByteSlice(t *testing.T) {
type JSONType struct {
Value NilByteSlice `json:"bytes"`
NilValue NilBoolean `json:"nilvalue,omitempty"`
}
expected := `{"bytes":"Ynl0ZXM=","nilvalue":null}`
jt := &JSONType{
Value: NewVariable([]byte("bytes")),
}
data, err := json.Marshal(&jt)
if err != nil {
t.Errorf("failed to marshal json with nil types: %s", err)
}
if !bytes.Equal(data, []byte(expected)) {
t.Errorf("expected json to be %q, got %q", expected, string(data))
}
}
func TestVariable_UnmarshalJSON_Float32(t *testing.T) { func TestVariable_UnmarshalJSON_Float32(t *testing.T) {
type JSONType struct { type JSONType struct {
Value NilFloat32 `json:"float32"` Value NilFloat32 `json:"float32"`
@ -111,6 +149,25 @@ func TestVariable_UnmarshalJSON_Float32(t *testing.T) {
} }
} }
func TestVariable_MarshalJSON_Float32(t *testing.T) {
type JSONType struct {
Value NilFloat32 `json:"float32"`
NilValue NilBoolean `json:"nilvalue,omitempty"`
}
expected := `{"float32":1.234,"nilvalue":null}`
jt := &JSONType{
Value: NewVariable(float32(1.234)),
}
data, err := json.Marshal(&jt)
if err != nil {
t.Errorf("failed to marshal json with nil types: %s", err)
}
if !bytes.Equal(data, []byte(expected)) {
t.Errorf("expected json to be %q, got %q", expected, string(data))
}
}
func TestVariable_UnmarshalJSON_Float64(t *testing.T) { func TestVariable_UnmarshalJSON_Float64(t *testing.T) {
type JSONType struct { type JSONType struct {
Value NilFloat64 `json:"float64"` Value NilFloat64 `json:"float64"`
@ -139,6 +196,25 @@ func TestVariable_UnmarshalJSON_Float64(t *testing.T) {
} }
} }
func TestVariable_MarshalJSON_Float64(t *testing.T) {
type JSONType struct {
Value NilFloat64 `json:"float64"`
NilValue NilBoolean `json:"nilvalue,omitempty"`
}
expected := `{"float64":123.456,"nilvalue":null}`
jt := &JSONType{
Value: NewVariable(123.456),
}
data, err := json.Marshal(&jt)
if err != nil {
t.Errorf("failed to marshal json with nil types: %s", err)
}
if !bytes.Equal(data, []byte(expected)) {
t.Errorf("expected json to be %q, got %q", expected, string(data))
}
}
func TestVariable_UnmarshalJSON_Int(t *testing.T) { func TestVariable_UnmarshalJSON_Int(t *testing.T) {
type JSONType struct { type JSONType struct {
Value NilInt `json:"int"` Value NilInt `json:"int"`
@ -167,6 +243,25 @@ func TestVariable_UnmarshalJSON_Int(t *testing.T) {
} }
} }
func TestVariable_MarshalJSON_Int(t *testing.T) {
type JSONType struct {
Value NilInt `json:"int"`
NilValue NilBoolean `json:"nilvalue,omitempty"`
}
expected := `{"int":123,"nilvalue":null}`
jt := &JSONType{
Value: NewVariable(123),
}
data, err := json.Marshal(&jt)
if err != nil {
t.Errorf("failed to marshal json with nil types: %s", err)
}
if !bytes.Equal(data, []byte(expected)) {
t.Errorf("expected json to be %q, got %q", expected, string(data))
}
}
func TestVariable_UnmarshalJSON_Int64(t *testing.T) { func TestVariable_UnmarshalJSON_Int64(t *testing.T) {
type JSONType struct { type JSONType struct {
Value NilInt64 `json:"int64"` Value NilInt64 `json:"int64"`
@ -195,6 +290,25 @@ func TestVariable_UnmarshalJSON_Int64(t *testing.T) {
} }
} }
func TestVariable_MarshalJSON_Int64(t *testing.T) {
type JSONType struct {
Value NilInt64 `json:"int64"`
NilValue NilBoolean `json:"nilvalue,omitempty"`
}
expected := `{"int64":12345678901234,"nilvalue":null}`
jt := &JSONType{
Value: NewVariable(int64(12345678901234)),
}
data, err := json.Marshal(&jt)
if err != nil {
t.Errorf("failed to marshal json with nil types: %s", err)
}
if !bytes.Equal(data, []byte(expected)) {
t.Errorf("expected json to be %q, got %q", expected, string(data))
}
}
func TestVariable_UnmarshalJSON_String(t *testing.T) { func TestVariable_UnmarshalJSON_String(t *testing.T) {
type JSONType struct { type JSONType struct {
Value NilString `json:"string"` Value NilString `json:"string"`
@ -223,6 +337,25 @@ func TestVariable_UnmarshalJSON_String(t *testing.T) {
} }
} }
func TestVariable_MarshalJSON_String(t *testing.T) {
type JSONType struct {
Value NilString `json:"string"`
NilValue NilBoolean `json:"nilvalue,omitempty"`
}
expected := `{"string":"test123","nilvalue":null}`
jt := &JSONType{
Value: NewVariable("test123"),
}
data, err := json.Marshal(&jt)
if err != nil {
t.Errorf("failed to marshal json with nil types: %s", err)
}
if !bytes.Equal(data, []byte(expected)) {
t.Errorf("expected json to be %q, got %q", expected, string(data))
}
}
func TestVariable_UnmarshalJSON_UInt(t *testing.T) { func TestVariable_UnmarshalJSON_UInt(t *testing.T) {
type JSONType struct { type JSONType struct {
Value NilUInt `json:"uint"` Value NilUInt `json:"uint"`
@ -251,6 +384,25 @@ func TestVariable_UnmarshalJSON_UInt(t *testing.T) {
} }
} }
func TestVariable_MarshalJSON_UInt(t *testing.T) {
type JSONType struct {
Value NilUInt `json:"uint"`
NilValue NilBoolean `json:"nilvalue,omitempty"`
}
expected := `{"uint":123,"nilvalue":null}`
jt := &JSONType{
Value: NewVariable(uint(123)),
}
data, err := json.Marshal(&jt)
if err != nil {
t.Errorf("failed to marshal json with nil types: %s", err)
}
if !bytes.Equal(data, []byte(expected)) {
t.Errorf("expected json to be %q, got %q", expected, string(data))
}
}
func TestVariable_UnmarshalJSON_UInt8(t *testing.T) { func TestVariable_UnmarshalJSON_UInt8(t *testing.T) {
type JSONType struct { type JSONType struct {
Value NilUInt8 `json:"uint8"` Value NilUInt8 `json:"uint8"`
@ -279,6 +431,25 @@ func TestVariable_UnmarshalJSON_UInt8(t *testing.T) {
} }
} }
func TestVariable_MarshalJSON_UInt8(t *testing.T) {
type JSONType struct {
Value NilUInt8 `json:"uint8"`
NilValue NilBoolean `json:"nilvalue,omitempty"`
}
expected := `{"uint8":1,"nilvalue":null}`
jt := &JSONType{
Value: NewVariable(uint8(1)),
}
data, err := json.Marshal(&jt)
if err != nil {
t.Errorf("failed to marshal json with nil types: %s", err)
}
if !bytes.Equal(data, []byte(expected)) {
t.Errorf("expected json to be %q, got %q", expected, string(data))
}
}
func TestVariable_UnmarshalJSON_UInt16(t *testing.T) { func TestVariable_UnmarshalJSON_UInt16(t *testing.T) {
type JSONType struct { type JSONType struct {
Value NilUInt16 `json:"uint16"` Value NilUInt16 `json:"uint16"`
@ -307,6 +478,25 @@ func TestVariable_UnmarshalJSON_UInt16(t *testing.T) {
} }
} }
func TestVariable_MarshalJSON_UInt16(t *testing.T) {
type JSONType struct {
Value NilUInt16 `json:"uint16"`
NilValue NilBoolean `json:"nilvalue,omitempty"`
}
expected := `{"uint16":2,"nilvalue":null}`
jt := &JSONType{
Value: NewVariable(uint16(2)),
}
data, err := json.Marshal(&jt)
if err != nil {
t.Errorf("failed to marshal json with nil types: %s", err)
}
if !bytes.Equal(data, []byte(expected)) {
t.Errorf("expected json to be %q, got %q", expected, string(data))
}
}
func TestVariable_UnmarshalJSON_UInt32(t *testing.T) { func TestVariable_UnmarshalJSON_UInt32(t *testing.T) {
type JSONType struct { type JSONType struct {
Value NilUInt32 `json:"uint32"` Value NilUInt32 `json:"uint32"`
@ -335,6 +525,25 @@ func TestVariable_UnmarshalJSON_UInt32(t *testing.T) {
} }
} }
func TestVariable_MarshalJSON_UInt32(t *testing.T) {
type JSONType struct {
Value NilUInt32 `json:"uint32"`
NilValue NilBoolean `json:"nilvalue,omitempty"`
}
expected := `{"uint32":3,"nilvalue":null}`
jt := &JSONType{
Value: NewVariable(uint32(3)),
}
data, err := json.Marshal(&jt)
if err != nil {
t.Errorf("failed to marshal json with nil types: %s", err)
}
if !bytes.Equal(data, []byte(expected)) {
t.Errorf("expected json to be %q, got %q", expected, string(data))
}
}
func TestVariable_UnmarshalJSON_UInt64(t *testing.T) { func TestVariable_UnmarshalJSON_UInt64(t *testing.T) {
type JSONType struct { type JSONType struct {
Value NilUInt64 `json:"uint64"` Value NilUInt64 `json:"uint64"`
@ -363,6 +572,25 @@ func TestVariable_UnmarshalJSON_UInt64(t *testing.T) {
} }
} }
func TestVariable_MarshalJSON_UInt64(t *testing.T) {
type JSONType struct {
Value NilUInt64 `json:"uint64"`
NilValue NilBoolean `json:"nilvalue,omitempty"`
}
expected := `{"uint64":4,"nilvalue":null}`
jt := &JSONType{
Value: NewVariable(uint64(4)),
}
data, err := json.Marshal(&jt)
if err != nil {
t.Errorf("failed to marshal json with nil types: %s", err)
}
if !bytes.Equal(data, []byte(expected)) {
t.Errorf("expected json to be %q, got %q", expected, string(data))
}
}
func ExampleVariable_UnmarshalJSON() { func ExampleVariable_UnmarshalJSON() {
type JSONType struct { type JSONType struct {
Bool NilBoolean `json:"bool"` Bool NilBoolean `json:"bool"`
@ -407,3 +635,35 @@ func ExampleVariable_UnmarshalJSON() {
fmt.Println(output) fmt.Println(output)
// Output: Bool is: true, Float 32 is nil, Float 64 is: 0.000000, String is: test // Output: Bool is: true, Float 32 is nil, Float 64 is: 0.000000, String is: test
} }
func ExampleVariable_MarshalJSON() {
type JSONType struct {
Bool NilBoolean `json:"bool"`
ByteSlice NilByteSlice `json:"bytes"`
Float32 NilFloat32 `json:"float32,omitempty"`
Float64 NilFloat64 `json:"float64"`
Int NilInt `json:"int"`
Int64 NilInt64 `json:"int64"`
NullString NilString `json:"nilvalue,omitempty"`
String NilString `json:"string"`
UInt NilUInt `json:"uint"`
UInt8 NilUInt8 `json:"uint8"`
}
example := &JSONType{
Bool: NewVariable(false),
ByteSlice: NewVariable([]byte("bytes")),
Float64: NewVariable(123.456),
Int: NewVariable(123),
Int64: NewVariable(int64(12345678901234)),
String: NewVariable("test"),
UInt: NewVariable(uint(123)),
}
data, err := json.Marshal(example)
if err != nil {
fmt.Printf("failed to marshal JSON: %s", err)
os.Exit(1)
}
fmt.Println(string(data))
// Output: {"bool":false,"bytes":"Ynl0ZXM=","float32":null,"float64":123.456,"int":123,"int64":12345678901234,"nilvalue":null,"string":"test","uint":123,"uint8":null}
}