diff --git a/common/jsonnumber.go b/common/jsonnumber.go new file mode 100644 index 00000000..85ddcbea --- /dev/null +++ b/common/jsonnumber.go @@ -0,0 +1,58 @@ +package common + +import ( + "encoding/json" + "strconv" + + "github.com/golang/glog" +) + +// JSONNumber is used instead of json.Number after upgrade to go 1.14 +// to handle data which can be numbers in double quotes or possibly not numbers at all +// see https://github.com/golang/go/issues/37308 +type JSONNumber string + +// Float64 returns JSONNumber as float64 +func (c JSONNumber) Float64() (float64, error) { + f, err := strconv.ParseFloat(string(c), 64) + if err != nil { + return 0, err + } + return f, nil +} + +// Int64 returns JSONNumber as int64 +func (c JSONNumber) Int64() (int64, error) { + i, err := strconv.ParseInt(string(c), 10, 64) + if err != nil { + return 0, err + } + return i, nil +} + +func (c JSONNumber) String() string { + return string(c) +} + +// MarshalJSON marsalls JSONNumber to []byte +// if possible, return a number without quotes, otherwise string value in quotes +func (c JSONNumber) MarshalJSON() ([]byte, error) { + if f, err := c.Float64(); err == nil { + return json.Marshal(f) + } + return json.Marshal(string(c)) +} + +// UnmarshalJSON unmarshalls JSONNumber from []byte +// if the value is in quotes, remove them +func (c *JSONNumber) UnmarshalJSON(d []byte) error { + s := string(d) + l := len(s) + if l > 1 && s[0] == '"' && s[l-1] == '"' { + *c = JSONNumber(s[1 : l-1]) + } else { + *c = JSONNumber(s) + } + glog.Info("JSONNumber ", s, ", ", *c) + return nil +} diff --git a/common/jsonnumber_test.go b/common/jsonnumber_test.go new file mode 100644 index 00000000..10945675 --- /dev/null +++ b/common/jsonnumber_test.go @@ -0,0 +1,52 @@ +package common + +import ( + "reflect" + "testing" +) + +func TestJSONNumber_MarshalJSON(t *testing.T) { + tests := []struct { + name string + c JSONNumber + want []byte + wantErr bool + }{ + {"0", JSONNumber("0"), []byte("0"), false}, + {"1", JSONNumber("1"), []byte("1"), false}, + {"2", JSONNumber("12341234.43214123"), []byte("12341234.43214123"), false}, + {"3", JSONNumber("123E55"), []byte("1.23e+57"), false}, + {"NaN", JSONNumber("dsfafdasf"), []byte("\"dsfafdasf\""), false}, + {"empty", JSONNumber(""), []byte("\"\""), false}, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + got, err := tt.c.MarshalJSON() + if (err != nil) != tt.wantErr { + t.Errorf("JSONNumber.MarshalJSON() error = %v, wantErr %v", err, tt.wantErr) + return + } + if !reflect.DeepEqual(got, tt.want) { + t.Errorf("JSONNumber.MarshalJSON() = %v, want %v", string(got), string(tt.want)) + } + }) + } +} + +func TestJSONNumber_UnmarshalJSON(t *testing.T) { + tests := []struct { + name string + c *JSONNumber + d []byte + wantErr bool + }{ + // TODO: Add test cases. + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + if err := tt.c.UnmarshalJSON(tt.d); (err != nil) != tt.wantErr { + t.Errorf("JSONNumber.UnmarshalJSON() error = %v, wantErr %v", err, tt.wantErr) + } + }) + } +}