Skip to content

Instantly share code, notes, and snippets.

@crgimenes
Last active December 14, 2023 21:17
Show Gist options
  • Save crgimenes/c3b8b4fcce8529e9201f83c8da134f32 to your computer and use it in GitHub Desktop.
Save crgimenes/c3b8b4fcce8529e9201f83c8da134f32 to your computer and use it in GitHub Desktop.
Golang Example of Receiving Arrays or Strings in Same JSON Object
package strarray
import (
"encoding/json"
"errors"
)
var (
// ErrUnsupportedType is returned if the type is not implemented
ErrUnsupportedType = errors.New("unsupported type")
)
// StrArray string array to be used on JSON UnmarshalJSON
type StrArray []string
// UnmarshalJSON convert JSON object array of string or
// a string format strings to a golang string array
func (sa *StrArray) UnmarshalJSON(data []byte) error {
var jsonObj interface{}
err := json.Unmarshal(data, &jsonObj)
if err != nil {
return err
}
switch obj := jsonObj.(type) {
case string:
*sa = StrArray([]string{obj})
return nil
case []interface{}:
s := make([]string, 0, len(obj))
for _, v := range obj {
value, ok := v.(string)
if !ok {
return ErrUnsupportedType
}
s = append(s, value)
}
*sa = StrArray(s)
return nil
}
return ErrUnsupportedType
}
package strarray
import (
"reflect"
"testing"
)
func TestStrArray_UnmarshalJSON(t *testing.T) {
type args struct {
data []byte
}
tests := []struct {
name string
sa *StrArray
args args
want StrArray
wantErr bool
}{
{
name: "simple string",
args: args{
data: []byte(`"test string"`),
},
sa: &StrArray{},
want: StrArray([]string{"test string"}),
},
{
name: "array string",
args: args{
data: []byte(`["test1","test2"]`),
},
sa: &StrArray{},
want: StrArray([]string{"test1", "test2"}),
},
{
name: "empty array",
args: args{
data: []byte(`[]`),
},
sa: &StrArray{},
want: StrArray([]string{}),
},
{
name: "empty array",
args: args{
data: []byte(`[]`),
},
sa: &StrArray{},
want: StrArray([]string{}),
},
{
name: "empty obj",
args: args{
data: []byte(`{}`),
},
sa: &StrArray{},
want: StrArray([]string{}),
wantErr: true,
},
{
name: "unsopported type",
args: args{
data: []byte(`[1,2,3]`),
},
sa: &StrArray{},
want: StrArray([]string{}),
wantErr: true,
},
{
name: "invalid JSON",
args: args{
data: []byte(`- [1,2,3]`),
},
sa: &StrArray{},
want: StrArray([]string{}),
wantErr: true,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
if err := tt.sa.UnmarshalJSON(tt.args.data); (err != nil) != tt.wantErr {
t.Errorf("StrArray.UnmarshalJSON() error = %v, wantErr %v", err, tt.wantErr)
}
if tt.wantErr {
return
}
if !reflect.DeepEqual(tt.sa, &tt.want) {
t.Errorf("StrArray.UnmarshalJSON() not equal = %v, want %v", tt.sa, tt.want)
}
})
}
}
@navossoc
Copy link

navossoc commented Sep 6, 2019

Acho que daria para simplificar um pouco mais o UnmarshalJSON e evitar alguns resizes do slice quando a entrada fosse o array de strings...

// UnmarshalJSON convert JSON object array of string or
// a string format strings to a golang string array
func (sa *StrArray) UnmarshalJSON(data []byte) error {
	var jsonObj interface{}
	err := json.Unmarshal(data, &jsonObj)
	if err != nil {
		return err
	}
	switch obj := jsonObj.(type) {
	case string:
		*sa = StrArray([]string{obj})
		return nil
	case []interface{}:
		s := make([]string, 0, len(obj))
		for _, v := range obj {
			value, ok := v.(string)
			if !ok {
				return ErrUnsupportedType
			}
			s = append(s, value)
		}
		*sa = StrArray(s)
		return nil
	}
	return ErrUnsupportedType
}

@crgimenes
Copy link
Author

Acho que daria para simplificar um pouco mais o UnmarshalJSON e evitar alguns resizes do slice quando a entrada fosse o array de strings...

Gostei! Vou aplicar sua sugestão :D

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment