From 5b1572d0fb38441892f242798cd8ba0e50f7ed77 Mon Sep 17 00:00:00 2001 From: soolaimon Date: Tue, 15 Jan 2019 16:31:53 -0700 Subject: [PATCH 01/17] add ability for UnmarshalJSON to conver string to int64 --- int64.go | 28 ++++++++++++++++++++++++---- int64_test.go | 8 +++++++- 2 files changed, 31 insertions(+), 5 deletions(-) diff --git a/int64.go b/int64.go index 5dfbee5..7ac5a82 100644 --- a/int64.go +++ b/int64.go @@ -4,6 +4,8 @@ import ( "bytes" "database/sql/driver" "encoding/json" + "fmt" + "reflect" "strconv" "github.com/volatiletech/null/convert" @@ -44,12 +46,30 @@ func (i *Int64) UnmarshalJSON(data []byte) error { return nil } - if err := json.Unmarshal(data, &i.Int64); err != nil { + var err error + var v interface{} + if err = json.Unmarshal(data, &v); err != nil { return err } - - i.Valid = true - return nil + switch x := v.(type) { + case float64: + // Unmarshal again, directly to int64, to avoid intermediate float64 + err = json.Unmarshal(data, &i.Int64) + case string: + str := string(x) + if len(str) == 0 { + i.Valid = false + return nil + } + i.Int64, err = strconv.ParseInt(str, 10, 64) + case nil: + i.Valid = false + return nil + default: + err = fmt.Errorf("json: cannot unmarshal %v into Go value of type null.Int", reflect.TypeOf(v).Name()) + } + i.Valid = err == nil + return err } // UnmarshalText implements encoding.TextUnmarshaler. diff --git a/int64_test.go b/int64_test.go index 8fb11a7..a3c4d7a 100644 --- a/int64_test.go +++ b/int64_test.go @@ -8,7 +8,8 @@ import ( ) var ( - int64JSON = []byte(`9223372036854775806`) + int64JSON = []byte(`9223372036854775806`) + int64StringJSON = []byte(`"9223372036854775806"`) ) func TestInt64From(t *testing.T) { @@ -37,6 +38,11 @@ func TestUnmarshalInt64(t *testing.T) { maybePanic(err) assertInt64(t, i, "int64 json") + var si Int64 + err = json.Unmarshal(int64StringJSON, &si) + maybePanic(err) + assertInt64(t, si, "int64 json") + var null Int64 err = json.Unmarshal(nullJSON, &null) maybePanic(err) From 59d2324a97bd1356df3d4981237f089ff6abe340 Mon Sep 17 00:00:00 2001 From: soolaimon Date: Wed, 16 Jan 2019 11:28:03 -0700 Subject: [PATCH 02/17] convert string to uint64 --- int64.go | 2 +- int64_test.go | 5 +++++ uint64.go | 27 ++++++++++++++++++++++++--- uint64_test.go | 15 +++++++++++++-- 4 files changed, 43 insertions(+), 6 deletions(-) diff --git a/int64.go b/int64.go index 7ac5a82..077e97c 100644 --- a/int64.go +++ b/int64.go @@ -68,7 +68,7 @@ func (i *Int64) UnmarshalJSON(data []byte) error { default: err = fmt.Errorf("json: cannot unmarshal %v into Go value of type null.Int", reflect.TypeOf(v).Name()) } - i.Valid = err == nil + i.Valid = (err == nil) && (i.Int64 != 0) return err } diff --git a/int64_test.go b/int64_test.go index a3c4d7a..c324476 100644 --- a/int64_test.go +++ b/int64_test.go @@ -48,6 +48,11 @@ func TestUnmarshalInt64(t *testing.T) { maybePanic(err) assertNullInt64(t, null, "null json") + var bi Int64 + err = json.Unmarshal(blankStringJSON, &bi) + maybePanic(err) + assertNullInt64(t, bi, "blank json string") + var badType Int64 err = json.Unmarshal(boolJSON, &badType) if err == nil { diff --git a/uint64.go b/uint64.go index 61a29dd..43e5476 100644 --- a/uint64.go +++ b/uint64.go @@ -4,6 +4,8 @@ import ( "bytes" "database/sql/driver" "encoding/json" + "fmt" + "reflect" "strconv" "github.com/volatiletech/null/convert" @@ -44,12 +46,31 @@ func (u *Uint64) UnmarshalJSON(data []byte) error { return nil } - if err := json.Unmarshal(data, &u.Uint64); err != nil { + var err error + var v interface{} + if err = json.Unmarshal(data, &v); err != nil { return err } + switch x := v.(type) { + case float64: + // Unmarshal again, directly to int64, to avoid intermediate float64 + err = json.Unmarshal(data, &u.Uint64) + case string: + str := string(x) + if len(str) == 0 { + u.Valid = false + return nil + } + u.Uint64, err = strconv.ParseUint(str, 10, 64) + case nil: + u.Valid = false + return nil + default: + err = fmt.Errorf("json: cannot unmarshal %v into Go value of type null.Int", reflect.TypeOf(v).Name()) + } - u.Valid = true - return nil + u.Valid = (err == nil) && (u.Uint64 != 0) + return err } // UnmarshalText implements encoding.TextUnmarshaler. diff --git a/uint64_test.go b/uint64_test.go index c0abc14..ca706d9 100644 --- a/uint64_test.go +++ b/uint64_test.go @@ -6,7 +6,8 @@ import ( ) var ( - uint64JSON = []byte(`18446744073709551614`) + uint64JSON = []byte(`18446744073709551614`) + uint64StringJSON = []byte(`"18446744073709551614"`) ) func TestUint64From(t *testing.T) { @@ -35,11 +36,21 @@ func TestUnmarshalUint64(t *testing.T) { maybePanic(err) assertUint64(t, i, "uint64 json") + var si Uint64 + err = json.Unmarshal(uint64StringJSON, &si) + maybePanic(err) + assertUint64(t, si, "uint64 json") + var null Uint64 - err = json.Unmarshal(nullJSON, &null) + err = json.Unmarshal(blankStringJSON, &null) maybePanic(err) assertNullUint64(t, null, "null json") + var bi Uint64 + err = json.Unmarshal(blankStringJSON, &bi) + maybePanic(err) + assertNullUint64(t, bi, "blank json string") + var badType Uint64 err = json.Unmarshal(boolJSON, &badType) if err == nil { From 24e451e15452d842780847483f836490490432fd Mon Sep 17 00:00:00 2001 From: soolaimon Date: Wed, 16 Jan 2019 12:57:38 -0700 Subject: [PATCH 03/17] adjust copy/pasted error msgs --- int64.go | 2 +- uint64.go | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/int64.go b/int64.go index 077e97c..59c8c28 100644 --- a/int64.go +++ b/int64.go @@ -66,7 +66,7 @@ func (i *Int64) UnmarshalJSON(data []byte) error { i.Valid = false return nil default: - err = fmt.Errorf("json: cannot unmarshal %v into Go value of type null.Int", reflect.TypeOf(v).Name()) + err = fmt.Errorf("json: cannot unmarshal %v into Go value of type null.Int64", reflect.TypeOf(v).Name()) } i.Valid = (err == nil) && (i.Int64 != 0) return err diff --git a/uint64.go b/uint64.go index 43e5476..7fb75ce 100644 --- a/uint64.go +++ b/uint64.go @@ -66,7 +66,7 @@ func (u *Uint64) UnmarshalJSON(data []byte) error { u.Valid = false return nil default: - err = fmt.Errorf("json: cannot unmarshal %v into Go value of type null.Int", reflect.TypeOf(v).Name()) + err = fmt.Errorf("json: cannot unmarshal %v into Go value of type null.Uint64", reflect.TypeOf(v).Name()) } u.Valid = (err == nil) && (u.Uint64 != 0) From 72ffc28976be93b64315ee3ca26571dd317bd6ab Mon Sep 17 00:00:00 2001 From: soolaimon Date: Thu, 17 Jan 2019 14:58:09 -0700 Subject: [PATCH 04/17] enable string -> uint32 for JSON --- int64.go | 1 + int64_test.go | 2 +- uint32.go | 36 +++++++++++++++++++++++++++++------- uint32_test.go | 13 ++++++++++++- uint64.go | 2 +- uint64_test.go | 2 +- 6 files changed, 45 insertions(+), 11 deletions(-) diff --git a/int64.go b/int64.go index 59c8c28..4048c2d 100644 --- a/int64.go +++ b/int64.go @@ -68,6 +68,7 @@ func (i *Int64) UnmarshalJSON(data []byte) error { default: err = fmt.Errorf("json: cannot unmarshal %v into Go value of type null.Int64", reflect.TypeOf(v).Name()) } + i.Valid = (err == nil) && (i.Int64 != 0) return err } diff --git a/int64_test.go b/int64_test.go index c324476..495a314 100644 --- a/int64_test.go +++ b/int64_test.go @@ -41,7 +41,7 @@ func TestUnmarshalInt64(t *testing.T) { var si Int64 err = json.Unmarshal(int64StringJSON, &si) maybePanic(err) - assertInt64(t, si, "int64 json") + assertInt64(t, si, "int64 string json") var null Int64 err = json.Unmarshal(nullJSON, &null) diff --git a/uint32.go b/uint32.go index 35cbbf1..b076a3b 100644 --- a/uint32.go +++ b/uint32.go @@ -6,6 +6,7 @@ import ( "encoding/json" "fmt" "math" + "reflect" "strconv" "github.com/volatiletech/null/convert" @@ -46,18 +47,39 @@ func (u *Uint32) UnmarshalJSON(data []byte) error { return nil } - var x uint64 - if err := json.Unmarshal(data, &x); err != nil { + var err error + var v interface{} + if err = json.Unmarshal(data, &v); err != nil { return err } - if x > math.MaxUint32 { - return fmt.Errorf("json: %d overflows max uint32 value", x) + var i uint64 + switch x := v.(type) { + case float64: + // Unmarshal again, directly to int64, to avoid intermediate float64 + err = json.Unmarshal(data, &i) + case string: + str := string(x) + if len(str) == 0 { + u.Valid = false + return nil + } + + i, err = strconv.ParseUint(str, 10, 32) + case nil: + u.Valid = false + return nil + default: + err = fmt.Errorf("json: cannot unmarshal %v into Go value of type null.Uint32", reflect.TypeOf(v).Name()) } - u.Uint32 = uint32(x) - u.Valid = true - return nil + if i > math.MaxUint32 { + return fmt.Errorf("json: %d overflows max uint32 value", i) + } + + u.Uint32 = uint32(i) + u.Valid = (err == nil) && (u.Uint32 != 0) + return err } // UnmarshalText implements encoding.TextUnmarshaler. diff --git a/uint32_test.go b/uint32_test.go index 27d25a0..c590060 100644 --- a/uint32_test.go +++ b/uint32_test.go @@ -8,7 +8,8 @@ import ( ) var ( - uint32JSON = []byte(`4294967294`) + uint32JSON = []byte(`4294967294`) + uint32StringJSON = []byte(`"4294967294"`) ) func TestUint32From(t *testing.T) { @@ -37,11 +38,21 @@ func TestUnmarshalUint32(t *testing.T) { maybePanic(err) assertUint32(t, i, "uint32 json") + var si Uint32 + err = json.Unmarshal(uint32StringJSON, &si) + maybePanic(err) + assertUint32(t, si, "uint32 string json") + var null Uint32 err = json.Unmarshal(nullJSON, &null) maybePanic(err) assertNullUint32(t, null, "null json") + var bi Uint32 + err = json.Unmarshal(blankStringJSON, &bi) + maybePanic(err) + assertNullUint32(t, bi, "blank json string") + var badType Uint32 err = json.Unmarshal(boolJSON, &badType) if err == nil { diff --git a/uint64.go b/uint64.go index 7fb75ce..d4f8634 100644 --- a/uint64.go +++ b/uint64.go @@ -53,7 +53,7 @@ func (u *Uint64) UnmarshalJSON(data []byte) error { } switch x := v.(type) { case float64: - // Unmarshal again, directly to int64, to avoid intermediate float64 + // Unmarshal again, directly to Uint64, to avoid intermediate float64 err = json.Unmarshal(data, &u.Uint64) case string: str := string(x) diff --git a/uint64_test.go b/uint64_test.go index ca706d9..122105b 100644 --- a/uint64_test.go +++ b/uint64_test.go @@ -39,7 +39,7 @@ func TestUnmarshalUint64(t *testing.T) { var si Uint64 err = json.Unmarshal(uint64StringJSON, &si) maybePanic(err) - assertUint64(t, si, "uint64 json") + assertUint64(t, si, "uint64 string json") var null Uint64 err = json.Unmarshal(blankStringJSON, &null) From 59508def598d3f7f33524d2d0d086798a98dd092 Mon Sep 17 00:00:00 2001 From: soolaimon Date: Thu, 17 Jan 2019 15:04:19 -0700 Subject: [PATCH 05/17] string -> uint8 json flexibility --- uint8.go | 37 ++++++++++++++++++++++++++++++------- uint8_test.go | 13 ++++++++++++- 2 files changed, 42 insertions(+), 8 deletions(-) diff --git a/uint8.go b/uint8.go index 1c6365c..d61dc8e 100644 --- a/uint8.go +++ b/uint8.go @@ -6,6 +6,7 @@ import ( "encoding/json" "fmt" "math" + "reflect" "strconv" "github.com/volatiletech/null/convert" @@ -46,18 +47,40 @@ func (u *Uint8) UnmarshalJSON(data []byte) error { return nil } - var x uint64 - if err := json.Unmarshal(data, &x); err != nil { + var err error + var v interface{} + if err = json.Unmarshal(data, &v); err != nil { return err } - if x > math.MaxUint8 { - return fmt.Errorf("json: %d overflows max uint8 value", x) + var i uint64 + switch x := v.(type) { + case float64: + // Unmarshal again, directly to int64, to avoid intermediate float64 + err = json.Unmarshal(data, &i) + case string: + str := string(x) + if len(str) == 0 { + u.Valid = false + return nil + } + + i, err = strconv.ParseUint(str, 10, 8) + case nil: + u.Valid = false + return nil + default: + err = fmt.Errorf("json: cannot unmarshal %v into Go value of type null.Uint8", reflect.TypeOf(v).Name()) } - u.Uint8 = uint8(x) - u.Valid = true - return nil + if i > math.MaxUint8 { + return fmt.Errorf("json: %d overflows max uint8 value", i) + } + + u.Uint8 = uint8(i) + u.Valid = (err == nil) && (u.Uint8 != 0) + return err + } // UnmarshalText implements encoding.TextUnmarshaler. diff --git a/uint8_test.go b/uint8_test.go index cb0b80a..cf0e866 100644 --- a/uint8_test.go +++ b/uint8_test.go @@ -8,7 +8,8 @@ import ( ) var ( - uint8JSON = []byte(`254`) + uint8JSON = []byte(`254`) + uint8StringJSON = []byte(`"254"`) ) func TestUint8From(t *testing.T) { @@ -37,11 +38,21 @@ func TestUnmarshalUint8(t *testing.T) { maybePanic(err) assertUint8(t, i, "uint8 json") + var si Uint8 + err = json.Unmarshal(uint8StringJSON, &si) + maybePanic(err) + assertUint8(t, si, "uint8 string json") + var null Uint8 err = json.Unmarshal(nullJSON, &null) maybePanic(err) assertNullUint8(t, null, "null json") + var bi Uint8 + err = json.Unmarshal(blankStringJSON, &bi) + maybePanic(err) + assertNullUint8(t, bi, "blank json string") + var badType Uint8 err = json.Unmarshal(boolJSON, &badType) if err == nil { From b6f30644d8dc437208fb78bdaa9b608e35bab692 Mon Sep 17 00:00:00 2001 From: soolaimon Date: Thu, 17 Jan 2019 15:06:37 -0700 Subject: [PATCH 06/17] string -> uint16 json flexibility --- uint16.go | 36 +++++++++++++++++++++++++++++------- uint16_test.go | 13 ++++++++++++- 2 files changed, 41 insertions(+), 8 deletions(-) diff --git a/uint16.go b/uint16.go index 41ca4f5..0df93f0 100644 --- a/uint16.go +++ b/uint16.go @@ -6,6 +6,7 @@ import ( "encoding/json" "fmt" "math" + "reflect" "strconv" "github.com/volatiletech/null/convert" @@ -46,18 +47,39 @@ func (u *Uint16) UnmarshalJSON(data []byte) error { return nil } - var x uint64 - if err := json.Unmarshal(data, &x); err != nil { + var err error + var v interface{} + if err = json.Unmarshal(data, &v); err != nil { return err } - if x > math.MaxUint16 { - return fmt.Errorf("json: %d overflows max uint8 value", x) + var i uint64 + switch x := v.(type) { + case float64: + // Unmarshal again, directly to int64, to avoid intermediate float64 + err = json.Unmarshal(data, &i) + case string: + str := string(x) + if len(str) == 0 { + u.Valid = false + return nil + } + + i, err = strconv.ParseUint(str, 10, 16) + case nil: + u.Valid = false + return nil + default: + err = fmt.Errorf("json: cannot unmarshal %v into Go value of type null.Uint16", reflect.TypeOf(v).Name()) } - u.Uint16 = uint16(x) - u.Valid = true - return nil + if i > math.MaxUint16 { + return fmt.Errorf("json: %d overflows max uint16 value", i) + } + + u.Uint16 = uint16(i) + u.Valid = (err == nil) && (u.Uint16 != 0) + return err } // UnmarshalText implements encoding.TextUnmarshaler. diff --git a/uint16_test.go b/uint16_test.go index e35d658..7bfb70e 100644 --- a/uint16_test.go +++ b/uint16_test.go @@ -8,7 +8,8 @@ import ( ) var ( - uint16JSON = []byte(`65534`) + uint16JSON = []byte(`65534`) + uint16StringJSON = []byte(`"65534"`) ) func TestUint16From(t *testing.T) { @@ -37,11 +38,21 @@ func TestUnmarshalUint16(t *testing.T) { maybePanic(err) assertUint16(t, i, "uint16 json") + var si Uint16 + err = json.Unmarshal(uint16StringJSON, &si) + maybePanic(err) + assertUint16(t, si, "uint16 string json") + var null Uint16 err = json.Unmarshal(nullJSON, &null) maybePanic(err) assertNullUint16(t, null, "null json") + var bi Uint16 + err = json.Unmarshal(blankStringJSON, &bi) + maybePanic(err) + assertNullUint16(t, bi, "blank json string") + var badType Uint16 err = json.Unmarshal(boolJSON, &badType) if err == nil { From 9ec23a90c5c88b1e3e0454c44e8416c2da8e56e0 Mon Sep 17 00:00:00 2001 From: soolaimon Date: Thu, 17 Jan 2019 15:13:05 -0700 Subject: [PATCH 07/17] string -> uint json flexibility --- uint.go | 33 ++++++++++++++++++++++++++++----- uint_test.go | 13 ++++++++++++- 2 files changed, 40 insertions(+), 6 deletions(-) diff --git a/uint.go b/uint.go index d750705..821cad7 100644 --- a/uint.go +++ b/uint.go @@ -4,6 +4,8 @@ import ( "bytes" "database/sql/driver" "encoding/json" + "fmt" + "reflect" "strconv" "github.com/volatiletech/null/convert" @@ -44,14 +46,35 @@ func (u *Uint) UnmarshalJSON(data []byte) error { return nil } - var x uint64 - if err := json.Unmarshal(data, &x); err != nil { + var err error + var v interface{} + if err = json.Unmarshal(data, &v); err != nil { return err } - u.Uint = uint(x) - u.Valid = true - return nil + var i uint64 + switch x := v.(type) { + case float64: + // Unmarshal again, directly to int64, to avoid intermediate float64 + err = json.Unmarshal(data, &i) + case string: + str := string(x) + if len(str) == 0 { + u.Valid = false + return nil + } + + i, err = strconv.ParseUint(str, 10, 64) + case nil: + u.Valid = false + return nil + default: + err = fmt.Errorf("json: cannot unmarshal %v into Go value of type null.Uint", reflect.TypeOf(v).Name()) + } + + u.Uint = uint(i) + u.Valid = (err == nil) && (u.Uint != 0) + return err } // UnmarshalText implements encoding.TextUnmarshaler. diff --git a/uint_test.go b/uint_test.go index 9ebd75f..0a7d281 100644 --- a/uint_test.go +++ b/uint_test.go @@ -6,7 +6,8 @@ import ( ) var ( - uintJSON = []byte(`12345`) + uintJSON = []byte(`12345`) + uintStringJSON = []byte(`"12345"`) ) func TestUintFrom(t *testing.T) { @@ -35,11 +36,21 @@ func TestUnmarshalUint(t *testing.T) { maybePanic(err) assertUint(t, i, "uint json") + var si Uint + err = json.Unmarshal(uintStringJSON, &si) + maybePanic(err) + assertUint(t, si, "uint string json") + var null Uint err = json.Unmarshal(nullJSON, &null) maybePanic(err) assertNullUint(t, null, "null json") + var bi Uint + err = json.Unmarshal(blankStringJSON, &bi) + maybePanic(err) + assertNullUint(t, bi, "blank json string") + var badType Uint err = json.Unmarshal(boolJSON, &badType) if err == nil { From 733a75af2e4732329e49976002f3acee06b7a79d Mon Sep 17 00:00:00 2001 From: soolaimon Date: Thu, 17 Jan 2019 15:25:26 -0700 Subject: [PATCH 08/17] string -> int json flexibility --- int.go | 29 ++++++++++++++++++++++++----- int_test.go | 13 ++++++++++++- 2 files changed, 36 insertions(+), 6 deletions(-) diff --git a/int.go b/int.go index 742c228..bb96817 100644 --- a/int.go +++ b/int.go @@ -4,7 +4,9 @@ import ( "bytes" "database/sql/driver" "encoding/json" + "fmt" "math" + "reflect" "strconv" "github.com/volatiletech/null/convert" @@ -45,14 +47,31 @@ func (i *Int) UnmarshalJSON(data []byte) error { return nil } - var x int64 - if err := json.Unmarshal(data, &x); err != nil { + var err error + var v interface{} + if err = json.Unmarshal(data, &v); err != nil { return err } + switch x := v.(type) { + case float64: + // Unmarshal again, directly to int64, to avoid intermediate float64 + err = json.Unmarshal(data, &i.Int) + case string: + str := string(x) + if len(str) == 0 { + i.Valid = false + return nil + } + i.Int, err = strconv.Atoi(str) + case nil: + i.Valid = false + return nil + default: + err = fmt.Errorf("json: cannot unmarshal %v into Go value of type null.Int64", reflect.TypeOf(v).Name()) + } - i.Int = int(x) - i.Valid = true - return nil + i.Valid = (err == nil) && (i.Int != 0) + return err } // UnmarshalText implements encoding.TextUnmarshaler. diff --git a/int_test.go b/int_test.go index d7aead9..7e0e71a 100644 --- a/int_test.go +++ b/int_test.go @@ -6,7 +6,8 @@ import ( ) var ( - intJSON = []byte(`12345`) + intJSON = []byte(`12345`) + intStringJSON = []byte(`"12345"`) ) func TestIntFrom(t *testing.T) { @@ -35,11 +36,21 @@ func TestUnmarshalInt(t *testing.T) { maybePanic(err) assertInt(t, i, "int json") + var si Int + err = json.Unmarshal(intStringJSON, &si) + maybePanic(err) + assertInt(t, si, "int string json") + var null Int err = json.Unmarshal(nullJSON, &null) maybePanic(err) assertNullInt(t, null, "null json") + var bi Int + err = json.Unmarshal(blankStringJSON, &bi) + maybePanic(err) + assertNullInt(t, bi, "blank json string") + var badType Int err = json.Unmarshal(boolJSON, &badType) if err == nil { From d9de0a5665f8950e0512f62a9764d54ac7d35567 Mon Sep 17 00:00:00 2001 From: soolaimon Date: Thu, 17 Jan 2019 15:26:14 -0700 Subject: [PATCH 09/17] update err msg --- int.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/int.go b/int.go index bb96817..e201ca9 100644 --- a/int.go +++ b/int.go @@ -67,7 +67,7 @@ func (i *Int) UnmarshalJSON(data []byte) error { i.Valid = false return nil default: - err = fmt.Errorf("json: cannot unmarshal %v into Go value of type null.Int64", reflect.TypeOf(v).Name()) + err = fmt.Errorf("json: cannot unmarshal %v into Go value of type null.Int", reflect.TypeOf(v).Name()) } i.Valid = (err == nil) && (i.Int != 0) From f51debbebb6b2fc045c0a6d280a510ce4b5328c9 Mon Sep 17 00:00:00 2001 From: soolaimon Date: Thu, 17 Jan 2019 15:54:46 -0700 Subject: [PATCH 10/17] string -> int32 json flexibility --- int32.go | 36 +++++++++++++++++++++++++++++------- int32_test.go | 13 ++++++++++++- 2 files changed, 41 insertions(+), 8 deletions(-) diff --git a/int32.go b/int32.go index eada3ef..4cee667 100644 --- a/int32.go +++ b/int32.go @@ -6,6 +6,7 @@ import ( "encoding/json" "fmt" "math" + "reflect" "strconv" "github.com/volatiletech/null/convert" @@ -47,18 +48,39 @@ func (i *Int32) UnmarshalJSON(data []byte) error { return nil } - var x int64 - if err := json.Unmarshal(data, &x); err != nil { + var err error + var v interface{} + if err = json.Unmarshal(data, &v); err != nil { return err } - if x > math.MaxInt32 { - return fmt.Errorf("json: %d overflows max int32 value", x) + var r int64 + switch x := v.(type) { + case float64: + // Unmarshal again, directly to int64, to avoid intermediate float64 + err = json.Unmarshal(data, &r) + case string: + str := string(x) + if len(str) == 0 { + i.Valid = false + return nil + } + + r, err = strconv.ParseInt(str, 10, 32) + case nil: + i.Valid = false + return nil + default: + err = fmt.Errorf("json: cannot unmarshal %v into Go value of type null.Int32", reflect.TypeOf(v).Name()) } - i.Int32 = int32(x) - i.Valid = true - return nil + if r > math.MaxInt32 { + return fmt.Errorf("json: %d overflows max int32 value", r) + } + + i.Int32 = int32(r) + i.Valid = (err == nil) && (i.Int32 != 0) + return err } // UnmarshalText implements encoding.TextUnmarshaler. diff --git a/int32_test.go b/int32_test.go index 683ca8d..77357f2 100644 --- a/int32_test.go +++ b/int32_test.go @@ -8,7 +8,8 @@ import ( ) var ( - int32JSON = []byte(`2147483646`) + int32JSON = []byte(`2147483646`) + int32StringJSON = []byte(`"2147483646"`) ) func TestInt32From(t *testing.T) { @@ -37,11 +38,21 @@ func TestUnmarshalInt32(t *testing.T) { maybePanic(err) assertInt32(t, i, "int32 json") + var si Int32 + err = json.Unmarshal(int32StringJSON, &si) + maybePanic(err) + assertInt32(t, si, "int32 string json") + var null Int32 err = json.Unmarshal(nullJSON, &null) maybePanic(err) assertNullInt32(t, null, "null json") + var bi Int32 + err = json.Unmarshal(blankStringJSON, &bi) + maybePanic(err) + assertNullInt32(t, bi, "blank json string") + var badType Int32 err = json.Unmarshal(boolJSON, &badType) if err == nil { From 48af80b06b53db60a422f474f4f4279be51fbf88 Mon Sep 17 00:00:00 2001 From: soolaimon Date: Thu, 17 Jan 2019 15:57:24 -0700 Subject: [PATCH 11/17] string -> int16 json flexibility --- int16.go | 36 +++++++++++++++++++++++++++++------- int16_test.go | 13 ++++++++++++- 2 files changed, 41 insertions(+), 8 deletions(-) diff --git a/int16.go b/int16.go index 445b20c..fb53e86 100644 --- a/int16.go +++ b/int16.go @@ -6,6 +6,7 @@ import ( "encoding/json" "fmt" "math" + "reflect" "strconv" "github.com/volatiletech/null/convert" @@ -46,18 +47,39 @@ func (i *Int16) UnmarshalJSON(data []byte) error { return nil } - var x int64 - if err := json.Unmarshal(data, &x); err != nil { + var err error + var v interface{} + if err = json.Unmarshal(data, &v); err != nil { return err } - if x > math.MaxInt16 { - return fmt.Errorf("json: %d overflows max int16 value", x) + var r int64 + switch x := v.(type) { + case float64: + // Unmarshal again, directly to int64, to avoid intermediate float64 + err = json.Unmarshal(data, &r) + case string: + str := string(x) + if len(str) == 0 { + i.Valid = false + return nil + } + + r, err = strconv.ParseInt(str, 10, 16) + case nil: + i.Valid = false + return nil + default: + err = fmt.Errorf("json: cannot unmarshal %v into Go value of type null.Int16", reflect.TypeOf(v).Name()) } - i.Int16 = int16(x) - i.Valid = true - return nil + if r > math.MaxInt16 { + return fmt.Errorf("json: %d overflows max int16 value", r) + } + + i.Int16 = int16(r) + i.Valid = (err == nil) && (i.Int16 != 0) + return err } // UnmarshalText implements encoding.TextUnmarshaler. diff --git a/int16_test.go b/int16_test.go index bbe1e32..78e035f 100644 --- a/int16_test.go +++ b/int16_test.go @@ -8,7 +8,8 @@ import ( ) var ( - int16JSON = []byte(`32766`) + int16JSON = []byte(`32766`) + int16StringJSON = []byte(`"32766"`) ) func TestInt16From(t *testing.T) { @@ -37,11 +38,21 @@ func TestUnmarshalInt16(t *testing.T) { maybePanic(err) assertInt16(t, i, "int16 json") + var si Int16 + err = json.Unmarshal(int16StringJSON, &si) + maybePanic(err) + assertInt16(t, si, "int16 string json") + var null Int16 err = json.Unmarshal(nullJSON, &null) maybePanic(err) assertNullInt16(t, null, "null json") + var bi Int16 + err = json.Unmarshal(blankStringJSON, &bi) + maybePanic(err) + assertNullInt16(t, bi, "blank json string") + var badType Int16 err = json.Unmarshal(boolJSON, &badType) if err == nil { From 1bd3976072836e719ad5a2fc797c1c4fcc590f24 Mon Sep 17 00:00:00 2001 From: soolaimon Date: Thu, 17 Jan 2019 15:59:58 -0700 Subject: [PATCH 12/17] string -> int8 json flexibility --- int8.go | 36 +++++++++++++++++++++++++++++------- int8_test.go | 13 ++++++++++++- 2 files changed, 41 insertions(+), 8 deletions(-) diff --git a/int8.go b/int8.go index c6682bb..9651582 100644 --- a/int8.go +++ b/int8.go @@ -6,6 +6,7 @@ import ( "encoding/json" "fmt" "math" + "reflect" "strconv" "github.com/volatiletech/null/convert" @@ -46,18 +47,39 @@ func (i *Int8) UnmarshalJSON(data []byte) error { return nil } - var x int64 - if err := json.Unmarshal(data, &x); err != nil { + var err error + var v interface{} + if err = json.Unmarshal(data, &v); err != nil { return err } - if x > math.MaxInt8 { - return fmt.Errorf("json: %d overflows max int8 value", x) + var r int64 + switch x := v.(type) { + case float64: + // Unmarshal again, directly to int64, to avoid intermediate float64 + err = json.Unmarshal(data, &r) + case string: + str := string(x) + if len(str) == 0 { + i.Valid = false + return nil + } + + r, err = strconv.ParseInt(str, 10, 8) + case nil: + i.Valid = false + return nil + default: + err = fmt.Errorf("json: cannot unmarshal %v into Go value of type null.Int8", reflect.TypeOf(v).Name()) } - i.Int8 = int8(x) - i.Valid = true - return nil + if r > math.MaxInt8 { + return fmt.Errorf("json: %d overflows max int8 value", r) + } + + i.Int8 = int8(r) + i.Valid = (err == nil) && (i.Int8 != 0) + return err } // UnmarshalText implements encoding.TextUnmarshaler. diff --git a/int8_test.go b/int8_test.go index 1c06a38..5503821 100644 --- a/int8_test.go +++ b/int8_test.go @@ -8,7 +8,8 @@ import ( ) var ( - int8JSON = []byte(`126`) + int8JSON = []byte(`126`) + int8StringJSON = []byte(`126`) ) func TestInt8From(t *testing.T) { @@ -37,11 +38,21 @@ func TestUnmarshalInt8(t *testing.T) { maybePanic(err) assertInt8(t, i, "int8 json") + var si Int8 + err = json.Unmarshal(int8StringJSON, &si) + maybePanic(err) + assertInt8(t, si, "int8 string json") + var null Int8 err = json.Unmarshal(nullJSON, &null) maybePanic(err) assertNullInt8(t, null, "null json") + var bi Int8 + err = json.Unmarshal(blankStringJSON, &bi) + maybePanic(err) + assertNullInt8(t, bi, "blank json string") + var badType Int8 err = json.Unmarshal(boolJSON, &badType) if err == nil { From 0ac9a03a9d945b40b0a5203924b90137e3b7b0d8 Mon Sep 17 00:00:00 2001 From: soolaimon Date: Fri, 18 Jan 2019 09:49:01 -0700 Subject: [PATCH 13/17] string -> float32 & float64 json flexibility --- float32.go | 37 ++++++++++++++++++++++++++++++++----- float32_test.go | 13 ++++++++++++- float64.go | 30 +++++++++++++++++++++++++----- float64_test.go | 13 ++++++++++++- 4 files changed, 81 insertions(+), 12 deletions(-) diff --git a/float32.go b/float32.go index d6373ba..ef0cfa6 100644 --- a/float32.go +++ b/float32.go @@ -4,6 +4,9 @@ import ( "bytes" "database/sql/driver" "encoding/json" + "fmt" + "math" + "reflect" "strconv" "github.com/volatiletech/null/convert" @@ -44,14 +47,38 @@ func (f *Float32) UnmarshalJSON(data []byte) error { return nil } - var x float64 - if err := json.Unmarshal(data, &x); err != nil { + var err error + var v interface{} + if err = json.Unmarshal(data, &v); err != nil { return err } - f.Float32 = float32(x) - f.Valid = true - return nil + var r float64 + switch x := v.(type) { + case float64: + r = v.(float64) + case string: + str := string(x) + if len(str) == 0 { + f.Valid = false + return nil + } + + r, err = strconv.ParseFloat(str, 32) + case nil: + f.Valid = false + return nil + default: + err = fmt.Errorf("json: cannot unmarshal %v into Go value of type null.Int32", reflect.TypeOf(v).Name()) + } + + if r > math.MaxFloat32 { + return fmt.Errorf("json: %f overflows max float32 value", r) + } + + f.Float32 = float32(r) + f.Valid = (err == nil) && (f.Float32 != 0) + return err } // UnmarshalText implements encoding.TextUnmarshaler. diff --git a/float32_test.go b/float32_test.go index c29350a..7586d3b 100644 --- a/float32_test.go +++ b/float32_test.go @@ -6,7 +6,8 @@ import ( ) var ( - float32JSON = []byte(`1.2345`) + float32JSON = []byte(`1.2345`) + float32StringJSON = []byte(`"1.2345"`) ) func TestFloat32From(t *testing.T) { @@ -35,11 +36,21 @@ func TestUnmarshalFloat32(t *testing.T) { maybePanic(err) assertFloat32(t, f, "float32 json") + var sf Float32 + err = json.Unmarshal(float32StringJSON, &sf) + maybePanic(err) + assertFloat32(t, sf, "float32 string json") + var null Float32 err = json.Unmarshal(nullJSON, &null) maybePanic(err) assertNullFloat32(t, null, "null json") + var bf Float32 + err = json.Unmarshal(blankStringJSON, &bf) + maybePanic(err) + assertNullFloat32(t, bf, "blank json string") + var badType Float32 err = json.Unmarshal(boolJSON, &badType) if err == nil { diff --git a/float64.go b/float64.go index 49d1355..8e245d2 100644 --- a/float64.go +++ b/float64.go @@ -4,6 +4,8 @@ import ( "bytes" "database/sql/driver" "encoding/json" + "fmt" + "reflect" "strconv" "github.com/volatiletech/null/convert" @@ -43,13 +45,31 @@ func (f *Float64) UnmarshalJSON(data []byte) error { f.Valid = false return nil } - - if err := json.Unmarshal(data, &f.Float64); err != nil { + var err error + var v interface{} + if err = json.Unmarshal(data, &v); err != nil { return err } - - f.Valid = true - return nil + switch x := v.(type) { + case float64: + f.Float64 = float64(x) + case string: + str := string(x) + if len(str) == 0 { + f.Valid = false + return nil + } + f.Float64, err = strconv.ParseFloat(str, 64) + case map[string]interface{}: + err = json.Unmarshal(data, &f.Float64) + case nil: + f.Valid = false + return nil + default: + err = fmt.Errorf("json: cannot unmarshal %v into Go value of type null.Float64", reflect.TypeOf(v).Name()) + } + f.Valid = err == nil + return err } // UnmarshalText implements encoding.TextUnmarshaler. diff --git a/float64_test.go b/float64_test.go index 11d20d0..24d4fad 100644 --- a/float64_test.go +++ b/float64_test.go @@ -6,7 +6,8 @@ import ( ) var ( - float64JSON = []byte(`1.2345`) + float64JSON = []byte(`1.2345`) + float64StringJSON = []byte(`"1.2345"`) ) func TestFloat64From(t *testing.T) { @@ -35,11 +36,21 @@ func TestUnmarshalFloat64(t *testing.T) { maybePanic(err) assertFloat64(t, f, "float64 json") + var sf Float64 + err = json.Unmarshal(float64StringJSON, &sf) + maybePanic(err) + assertFloat64(t, sf, "float64 string json") + var null Float64 err = json.Unmarshal(nullJSON, &null) maybePanic(err) assertNullFloat64(t, null, "null json") + var bf Float64 + err = json.Unmarshal(blankStringJSON, &bf) + maybePanic(err) + assertNullFloat64(t, bf, "blank json string") + var badType Float64 err = json.Unmarshal(boolJSON, &badType) if err == nil { From 5c97a070a31bb2d1f7d39ed9c7afb6665b7476f9 Mon Sep 17 00:00:00 2001 From: soolaimon Date: Fri, 18 Jan 2019 09:51:54 -0700 Subject: [PATCH 14/17] fix error msg --- float32.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/float32.go b/float32.go index ef0cfa6..8096ffc 100644 --- a/float32.go +++ b/float32.go @@ -69,7 +69,7 @@ func (f *Float32) UnmarshalJSON(data []byte) error { f.Valid = false return nil default: - err = fmt.Errorf("json: cannot unmarshal %v into Go value of type null.Int32", reflect.TypeOf(v).Name()) + err = fmt.Errorf("json: cannot unmarshal %v into Go value of type null.Float32", reflect.TypeOf(v).Name()) } if r > math.MaxFloat32 { From 3d6a39923638af8cf103a0273d7988c809479906 Mon Sep 17 00:00:00 2001 From: soolaimon Date: Fri, 18 Jan 2019 09:56:27 -0700 Subject: [PATCH 15/17] update comments --- int.go | 2 +- int32.go | 2 +- uint.go | 2 +- uint16.go | 2 +- uint32.go | 2 +- uint64.go | 2 +- uint8.go | 2 +- 7 files changed, 7 insertions(+), 7 deletions(-) diff --git a/int.go b/int.go index e201ca9..db8eb33 100644 --- a/int.go +++ b/int.go @@ -54,7 +54,7 @@ func (i *Int) UnmarshalJSON(data []byte) error { } switch x := v.(type) { case float64: - // Unmarshal again, directly to int64, to avoid intermediate float64 + // Unmarshal again, directly to int, to avoid intermediate float64 err = json.Unmarshal(data, &i.Int) case string: str := string(x) diff --git a/int32.go b/int32.go index 4cee667..9ef05e9 100644 --- a/int32.go +++ b/int32.go @@ -57,7 +57,7 @@ func (i *Int32) UnmarshalJSON(data []byte) error { var r int64 switch x := v.(type) { case float64: - // Unmarshal again, directly to int64, to avoid intermediate float64 + // Unmarshal again, directly to uint64, to avoid intermediate float64 err = json.Unmarshal(data, &r) case string: str := string(x) diff --git a/uint.go b/uint.go index 821cad7..f97b446 100644 --- a/uint.go +++ b/uint.go @@ -55,7 +55,7 @@ func (u *Uint) UnmarshalJSON(data []byte) error { var i uint64 switch x := v.(type) { case float64: - // Unmarshal again, directly to int64, to avoid intermediate float64 + // Unmarshal again, directly to uint, to avoid intermediate float64 err = json.Unmarshal(data, &i) case string: str := string(x) diff --git a/uint16.go b/uint16.go index 0df93f0..b62d5e5 100644 --- a/uint16.go +++ b/uint16.go @@ -56,7 +56,7 @@ func (u *Uint16) UnmarshalJSON(data []byte) error { var i uint64 switch x := v.(type) { case float64: - // Unmarshal again, directly to int64, to avoid intermediate float64 + // Unmarshal again, directly to uint64, to avoid intermediate float64 err = json.Unmarshal(data, &i) case string: str := string(x) diff --git a/uint32.go b/uint32.go index b076a3b..d43f419 100644 --- a/uint32.go +++ b/uint32.go @@ -56,7 +56,7 @@ func (u *Uint32) UnmarshalJSON(data []byte) error { var i uint64 switch x := v.(type) { case float64: - // Unmarshal again, directly to int64, to avoid intermediate float64 + // Unmarshal again, directly to uint64, to avoid intermediate float64 err = json.Unmarshal(data, &i) case string: str := string(x) diff --git a/uint64.go b/uint64.go index d4f8634..857f692 100644 --- a/uint64.go +++ b/uint64.go @@ -53,7 +53,7 @@ func (u *Uint64) UnmarshalJSON(data []byte) error { } switch x := v.(type) { case float64: - // Unmarshal again, directly to Uint64, to avoid intermediate float64 + // Unmarshal again, directly to uint64, to avoid intermediate float64 err = json.Unmarshal(data, &u.Uint64) case string: str := string(x) diff --git a/uint8.go b/uint8.go index d61dc8e..389a127 100644 --- a/uint8.go +++ b/uint8.go @@ -56,7 +56,7 @@ func (u *Uint8) UnmarshalJSON(data []byte) error { var i uint64 switch x := v.(type) { case float64: - // Unmarshal again, directly to int64, to avoid intermediate float64 + // Unmarshal again, directly to uint64, to avoid intermediate float64 err = json.Unmarshal(data, &i) case string: str := string(x) From 135d7a0b668b851e17d300f0cd74cf8634f21407 Mon Sep 17 00:00:00 2001 From: soolaimon Date: Fri, 18 Jan 2019 10:06:00 -0700 Subject: [PATCH 16/17] fix constant --- uint64_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/uint64_test.go b/uint64_test.go index 122105b..590c364 100644 --- a/uint64_test.go +++ b/uint64_test.go @@ -42,7 +42,7 @@ func TestUnmarshalUint64(t *testing.T) { assertUint64(t, si, "uint64 string json") var null Uint64 - err = json.Unmarshal(blankStringJSON, &null) + err = json.Unmarshal(nullJSON, &null) maybePanic(err) assertNullUint64(t, null, "null json") From 493e7ad49733be222603c86e0ed1f8011de87a2c Mon Sep 17 00:00:00 2001 From: soolaimon Date: Thu, 24 Jan 2019 08:58:37 -0700 Subject: [PATCH 17/17] remove unneeded case --- float64.go | 2 -- 1 file changed, 2 deletions(-) diff --git a/float64.go b/float64.go index 8e245d2..effc101 100644 --- a/float64.go +++ b/float64.go @@ -60,8 +60,6 @@ func (f *Float64) UnmarshalJSON(data []byte) error { return nil } f.Float64, err = strconv.ParseFloat(str, 64) - case map[string]interface{}: - err = json.Unmarshal(data, &f.Float64) case nil: f.Valid = false return nil