This is an automated email from the ASF dual-hosted git repository.
zeroshade pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/arrow-go.git
The following commit(s) were added to refs/heads/main by this push:
new 8d81fc39 fix(arrow/array): handle exponent notation for unmarshal int
(#662)
8d81fc39 is described below
commit 8d81fc39254b7a51daf1fe1a272c24169a059878
Author: Matt Topol <[email protected]>
AuthorDate: Wed Feb 11 17:19:53 2026 -0500
fix(arrow/array): handle exponent notation for unmarshal int (#662)
### Rationale for this change
fixes #474
### What changes are included in this PR?
Modify unmarshal methods to try parseint/parseuint first, and to
fallback to ParseFloat to handle exponential notation.
### Are these changes tested?
A test is added to confirm
### Are there any user-facing changes?
Just the bug fix
---
arrow/array/json_reader_test.go | 72 +++++++
arrow/array/numericbuilder.gen.go | 364 +++++++++++++++++++++++++++++++++
arrow/array/numericbuilder.gen.go.tmpl | 103 ++++++++++
3 files changed, 539 insertions(+)
diff --git a/arrow/array/json_reader_test.go b/arrow/array/json_reader_test.go
index 854c8eac..d309b0c3 100644
--- a/arrow/array/json_reader_test.go
+++ b/arrow/array/json_reader_test.go
@@ -143,6 +143,78 @@ func TestUnmarshalJSON(t *testing.T) {
assert.NotNil(t, record)
}
+func TestJSONReaderExponentialNotation(t *testing.T) {
+ tests := []struct {
+ name string
+ dataType arrow.DataType
+ input string
+ expected interface{}
+ }{
+ {
+ name: "int64 exponential notation",
+ dataType: arrow.PrimitiveTypes.Int64,
+ input: `{"value":"6.6999677E+8"}`,
+ expected: int64(669996770),
+ },
+ {
+ name: "uint64 exponential notation",
+ dataType: arrow.PrimitiveTypes.Uint64,
+ input: `{"value":"6.6999677E+8"}`,
+ expected: uint64(669996770),
+ },
+ {
+ name: "int64 lowercase exponential",
+ dataType: arrow.PrimitiveTypes.Int64,
+ input: `{"value":"1.5e+3"}`,
+ expected: int64(1500),
+ },
+ {
+ name: "uint64 negative exponent",
+ dataType: arrow.PrimitiveTypes.Uint64,
+ input: `{"value":"1.5e+3"}`,
+ expected: uint64(1500),
+ },
+ }
+
+ for _, tt := range tests {
+ t.Run(tt.name, func(t *testing.T) {
+ schema := arrow.NewSchema([]arrow.Field{
+ {Name: "value", Type: tt.dataType, Nullable:
true},
+ }, nil)
+
+ mem :=
memory.NewCheckedAllocator(memory.NewGoAllocator())
+ defer mem.AssertSize(t, 0)
+
+ recordBuilder := array.NewRecordBuilder(mem, schema)
+ defer recordBuilder.Release()
+
+ err := recordBuilder.UnmarshalJSON([]byte(tt.input))
+ if !assert.NoError(t, err, "should parse exponential
notation") {
+ return
+ }
+
+ record := recordBuilder.NewRecordBatch()
+ defer record.Release()
+
+ if !assert.Equal(t, int64(1), record.NumRows()) {
+ return
+ }
+ col := record.Column(0)
+
+ switch v := tt.expected.(type) {
+ case int64:
+ intCol, ok := col.(*array.Int64)
+ assert.True(t, ok)
+ assert.Equal(t, v, intCol.Value(0))
+ case uint64:
+ uintCol, ok := col.(*array.Uint64)
+ assert.True(t, ok)
+ assert.Equal(t, v, uintCol.Value(0))
+ }
+ })
+ }
+}
+
func generateJSONData(n int) []byte {
records := make([]map[string]any, n)
for i := range n {
diff --git a/arrow/array/numericbuilder.gen.go
b/arrow/array/numericbuilder.gen.go
index be87fbf4..aa2f628d 100644
--- a/arrow/array/numericbuilder.gen.go
+++ b/arrow/array/numericbuilder.gen.go
@@ -218,7 +218,29 @@ func (b *Int64Builder) UnmarshalOne(dec *json.Decoder)
error {
b.AppendNull()
case string:
+ // Try ParseInt first for direct integer strings, fall back to
ParseFloat for exponential notation
f, err := strconv.ParseInt(v, 10, 8*8)
+ if err != nil {
+ // Could be exponential notation - try parsing as float
+ fval, ferr := strconv.ParseFloat(v, 64)
+ if ferr != nil {
+ return &json.UnmarshalTypeError{
+ Value: v,
+ Type: reflect.TypeOf(int64(0)),
+ Offset: dec.InputOffset(),
+ }
+ }
+ // Check if it's a whole number and in valid range
+ if fval != float64(int64(fval)) || fval <
-9223372036854775808.0 || fval >= 9223372036854775808.0 {
+ return &json.UnmarshalTypeError{
+ Value: v,
+ Type: reflect.TypeOf(int64(0)),
+ Offset: dec.InputOffset(),
+ }
+ }
+ f = int64(fval)
+ err = nil // Clear error after successful float parsing
+ }
if err != nil {
return &json.UnmarshalTypeError{
Value: v,
@@ -230,7 +252,29 @@ func (b *Int64Builder) UnmarshalOne(dec *json.Decoder)
error {
case float64:
b.Append(int64(v))
case json.Number:
+ // Try ParseInt first for direct integer strings, fall back to
ParseFloat for exponential notation
f, err := strconv.ParseInt(v.String(), 10, 8*8)
+ if err != nil {
+ // Could be exponential notation - try parsing as float
+ fval, ferr := strconv.ParseFloat(v.String(), 64)
+ if ferr != nil {
+ return &json.UnmarshalTypeError{
+ Value: v.String(),
+ Type: reflect.TypeOf(int64(0)),
+ Offset: dec.InputOffset(),
+ }
+ }
+ // Check if it's a whole number and in valid range
+ if fval != float64(int64(fval)) || fval <
-9223372036854775808.0 || fval >= 9223372036854775808.0 {
+ return &json.UnmarshalTypeError{
+ Value: v.String(),
+ Type: reflect.TypeOf(int64(0)),
+ Offset: dec.InputOffset(),
+ }
+ }
+ f = int64(fval)
+ err = nil // Clear error after successful float parsing
+ }
if err != nil {
return &json.UnmarshalTypeError{
Value: v.String(),
@@ -459,7 +503,29 @@ func (b *Uint64Builder) UnmarshalOne(dec *json.Decoder)
error {
b.AppendNull()
case string:
+ // Try ParseUint first for direct integer strings, fall back to
ParseFloat for exponential notation
f, err := strconv.ParseUint(v, 10, 8*8)
+ if err != nil {
+ // Could be exponential notation - try parsing as float
+ fval, ferr := strconv.ParseFloat(v, 64)
+ if ferr != nil {
+ return &json.UnmarshalTypeError{
+ Value: v,
+ Type: reflect.TypeOf(uint64(0)),
+ Offset: dec.InputOffset(),
+ }
+ }
+ // Check if it's a whole number and in valid range
+ if fval != float64(uint64(fval)) || fval < 0 || fval >
float64(^uint64(0)) {
+ return &json.UnmarshalTypeError{
+ Value: v,
+ Type: reflect.TypeOf(uint64(0)),
+ Offset: dec.InputOffset(),
+ }
+ }
+ f = uint64(fval)
+ err = nil // Clear error after successful float parsing
+ }
if err != nil {
return &json.UnmarshalTypeError{
Value: v,
@@ -471,7 +537,29 @@ func (b *Uint64Builder) UnmarshalOne(dec *json.Decoder)
error {
case float64:
b.Append(uint64(v))
case json.Number:
+ // Try ParseUint first for direct integer strings, fall back to
ParseFloat for exponential notation
f, err := strconv.ParseUint(v.String(), 10, 8*8)
+ if err != nil {
+ // Could be exponential notation - try parsing as float
+ fval, ferr := strconv.ParseFloat(v.String(), 64)
+ if ferr != nil {
+ return &json.UnmarshalTypeError{
+ Value: v.String(),
+ Type: reflect.TypeOf(uint64(0)),
+ Offset: dec.InputOffset(),
+ }
+ }
+ // Check if it's a whole number and in valid range
+ if fval != float64(uint64(fval)) || fval < 0 || fval >
float64(^uint64(0)) {
+ return &json.UnmarshalTypeError{
+ Value: v.String(),
+ Type: reflect.TypeOf(uint64(0)),
+ Offset: dec.InputOffset(),
+ }
+ }
+ f = uint64(fval)
+ err = nil // Clear error after successful float parsing
+ }
if err != nil {
return &json.UnmarshalTypeError{
Value: v.String(),
@@ -941,7 +1029,31 @@ func (b *Int32Builder) UnmarshalOne(dec *json.Decoder)
error {
b.AppendNull()
case string:
+ // Try ParseInt first for direct integer strings, fall back to
ParseFloat for exponential notation
f, err := strconv.ParseInt(v, 10, 4*8)
+ if err != nil {
+ // Could be exponential notation - try parsing as float
+ fval, ferr := strconv.ParseFloat(v, 64)
+ if ferr != nil {
+ return &json.UnmarshalTypeError{
+ Value: v,
+ Type: reflect.TypeOf(int32(0)),
+ Offset: dec.InputOffset(),
+ }
+ }
+ // Check if it's a whole number and in valid range
+ minVal := float64(int64(-1) << (4*8 - 1))
+ maxVal := float64(int64(1) << (4*8 - 1))
+ if fval != float64(int64(fval)) || fval < minVal ||
fval >= maxVal {
+ return &json.UnmarshalTypeError{
+ Value: v,
+ Type: reflect.TypeOf(int32(0)),
+ Offset: dec.InputOffset(),
+ }
+ }
+ f = int64(fval)
+ err = nil // Clear error after successful float parsing
+ }
if err != nil {
return &json.UnmarshalTypeError{
Value: v,
@@ -953,7 +1065,31 @@ func (b *Int32Builder) UnmarshalOne(dec *json.Decoder)
error {
case float64:
b.Append(int32(v))
case json.Number:
+ // Try ParseInt first for direct integer strings, fall back to
ParseFloat for exponential notation
f, err := strconv.ParseInt(v.String(), 10, 4*8)
+ if err != nil {
+ // Could be exponential notation - try parsing as float
+ fval, ferr := strconv.ParseFloat(v.String(), 64)
+ if ferr != nil {
+ return &json.UnmarshalTypeError{
+ Value: v.String(),
+ Type: reflect.TypeOf(int32(0)),
+ Offset: dec.InputOffset(),
+ }
+ }
+ // Check if it's a whole number and in valid range
+ minVal := float64(int64(-1) << (4*8 - 1))
+ maxVal := float64(int64(1) << (4*8 - 1))
+ if fval != float64(int64(fval)) || fval < minVal ||
fval >= maxVal {
+ return &json.UnmarshalTypeError{
+ Value: v.String(),
+ Type: reflect.TypeOf(int32(0)),
+ Offset: dec.InputOffset(),
+ }
+ }
+ f = int64(fval)
+ err = nil // Clear error after successful float parsing
+ }
if err != nil {
return &json.UnmarshalTypeError{
Value: v.String(),
@@ -1182,7 +1318,29 @@ func (b *Uint32Builder) UnmarshalOne(dec *json.Decoder)
error {
b.AppendNull()
case string:
+ // Try ParseUint first for direct integer strings, fall back to
ParseFloat for exponential notation
f, err := strconv.ParseUint(v, 10, 4*8)
+ if err != nil {
+ // Could be exponential notation - try parsing as float
+ fval, ferr := strconv.ParseFloat(v, 64)
+ if ferr != nil {
+ return &json.UnmarshalTypeError{
+ Value: v,
+ Type: reflect.TypeOf(uint32(0)),
+ Offset: dec.InputOffset(),
+ }
+ }
+ // Check if it's a whole number and in valid range
+ if fval != float64(uint64(fval)) || fval < 0 || fval >
float64(^uint32(0)) {
+ return &json.UnmarshalTypeError{
+ Value: v,
+ Type: reflect.TypeOf(uint32(0)),
+ Offset: dec.InputOffset(),
+ }
+ }
+ f = uint64(fval)
+ err = nil // Clear error after successful float parsing
+ }
if err != nil {
return &json.UnmarshalTypeError{
Value: v,
@@ -1194,7 +1352,29 @@ func (b *Uint32Builder) UnmarshalOne(dec *json.Decoder)
error {
case float64:
b.Append(uint32(v))
case json.Number:
+ // Try ParseUint first for direct integer strings, fall back to
ParseFloat for exponential notation
f, err := strconv.ParseUint(v.String(), 10, 4*8)
+ if err != nil {
+ // Could be exponential notation - try parsing as float
+ fval, ferr := strconv.ParseFloat(v.String(), 64)
+ if ferr != nil {
+ return &json.UnmarshalTypeError{
+ Value: v.String(),
+ Type: reflect.TypeOf(uint32(0)),
+ Offset: dec.InputOffset(),
+ }
+ }
+ // Check if it's a whole number and in valid range
+ if fval != float64(uint64(fval)) || fval < 0 || fval >
float64(^uint32(0)) {
+ return &json.UnmarshalTypeError{
+ Value: v.String(),
+ Type: reflect.TypeOf(uint32(0)),
+ Offset: dec.InputOffset(),
+ }
+ }
+ f = uint64(fval)
+ err = nil // Clear error after successful float parsing
+ }
if err != nil {
return &json.UnmarshalTypeError{
Value: v.String(),
@@ -1664,7 +1844,31 @@ func (b *Int16Builder) UnmarshalOne(dec *json.Decoder)
error {
b.AppendNull()
case string:
+ // Try ParseInt first for direct integer strings, fall back to
ParseFloat for exponential notation
f, err := strconv.ParseInt(v, 10, 2*8)
+ if err != nil {
+ // Could be exponential notation - try parsing as float
+ fval, ferr := strconv.ParseFloat(v, 64)
+ if ferr != nil {
+ return &json.UnmarshalTypeError{
+ Value: v,
+ Type: reflect.TypeOf(int16(0)),
+ Offset: dec.InputOffset(),
+ }
+ }
+ // Check if it's a whole number and in valid range
+ minVal := float64(int64(-1) << (2*8 - 1))
+ maxVal := float64(int64(1) << (2*8 - 1))
+ if fval != float64(int64(fval)) || fval < minVal ||
fval >= maxVal {
+ return &json.UnmarshalTypeError{
+ Value: v,
+ Type: reflect.TypeOf(int16(0)),
+ Offset: dec.InputOffset(),
+ }
+ }
+ f = int64(fval)
+ err = nil // Clear error after successful float parsing
+ }
if err != nil {
return &json.UnmarshalTypeError{
Value: v,
@@ -1676,7 +1880,31 @@ func (b *Int16Builder) UnmarshalOne(dec *json.Decoder)
error {
case float64:
b.Append(int16(v))
case json.Number:
+ // Try ParseInt first for direct integer strings, fall back to
ParseFloat for exponential notation
f, err := strconv.ParseInt(v.String(), 10, 2*8)
+ if err != nil {
+ // Could be exponential notation - try parsing as float
+ fval, ferr := strconv.ParseFloat(v.String(), 64)
+ if ferr != nil {
+ return &json.UnmarshalTypeError{
+ Value: v.String(),
+ Type: reflect.TypeOf(int16(0)),
+ Offset: dec.InputOffset(),
+ }
+ }
+ // Check if it's a whole number and in valid range
+ minVal := float64(int64(-1) << (2*8 - 1))
+ maxVal := float64(int64(1) << (2*8 - 1))
+ if fval != float64(int64(fval)) || fval < minVal ||
fval >= maxVal {
+ return &json.UnmarshalTypeError{
+ Value: v.String(),
+ Type: reflect.TypeOf(int16(0)),
+ Offset: dec.InputOffset(),
+ }
+ }
+ f = int64(fval)
+ err = nil // Clear error after successful float parsing
+ }
if err != nil {
return &json.UnmarshalTypeError{
Value: v.String(),
@@ -1905,7 +2133,29 @@ func (b *Uint16Builder) UnmarshalOne(dec *json.Decoder)
error {
b.AppendNull()
case string:
+ // Try ParseUint first for direct integer strings, fall back to
ParseFloat for exponential notation
f, err := strconv.ParseUint(v, 10, 2*8)
+ if err != nil {
+ // Could be exponential notation - try parsing as float
+ fval, ferr := strconv.ParseFloat(v, 64)
+ if ferr != nil {
+ return &json.UnmarshalTypeError{
+ Value: v,
+ Type: reflect.TypeOf(uint16(0)),
+ Offset: dec.InputOffset(),
+ }
+ }
+ // Check if it's a whole number and in valid range
+ if fval != float64(uint64(fval)) || fval < 0 || fval >
float64(^uint16(0)) {
+ return &json.UnmarshalTypeError{
+ Value: v,
+ Type: reflect.TypeOf(uint16(0)),
+ Offset: dec.InputOffset(),
+ }
+ }
+ f = uint64(fval)
+ err = nil // Clear error after successful float parsing
+ }
if err != nil {
return &json.UnmarshalTypeError{
Value: v,
@@ -1917,7 +2167,29 @@ func (b *Uint16Builder) UnmarshalOne(dec *json.Decoder)
error {
case float64:
b.Append(uint16(v))
case json.Number:
+ // Try ParseUint first for direct integer strings, fall back to
ParseFloat for exponential notation
f, err := strconv.ParseUint(v.String(), 10, 2*8)
+ if err != nil {
+ // Could be exponential notation - try parsing as float
+ fval, ferr := strconv.ParseFloat(v.String(), 64)
+ if ferr != nil {
+ return &json.UnmarshalTypeError{
+ Value: v.String(),
+ Type: reflect.TypeOf(uint16(0)),
+ Offset: dec.InputOffset(),
+ }
+ }
+ // Check if it's a whole number and in valid range
+ if fval != float64(uint64(fval)) || fval < 0 || fval >
float64(^uint16(0)) {
+ return &json.UnmarshalTypeError{
+ Value: v.String(),
+ Type: reflect.TypeOf(uint16(0)),
+ Offset: dec.InputOffset(),
+ }
+ }
+ f = uint64(fval)
+ err = nil // Clear error after successful float parsing
+ }
if err != nil {
return &json.UnmarshalTypeError{
Value: v.String(),
@@ -2146,7 +2418,31 @@ func (b *Int8Builder) UnmarshalOne(dec *json.Decoder)
error {
b.AppendNull()
case string:
+ // Try ParseInt first for direct integer strings, fall back to
ParseFloat for exponential notation
f, err := strconv.ParseInt(v, 10, 1*8)
+ if err != nil {
+ // Could be exponential notation - try parsing as float
+ fval, ferr := strconv.ParseFloat(v, 64)
+ if ferr != nil {
+ return &json.UnmarshalTypeError{
+ Value: v,
+ Type: reflect.TypeOf(int8(0)),
+ Offset: dec.InputOffset(),
+ }
+ }
+ // Check if it's a whole number and in valid range
+ minVal := float64(int64(-1) << (1*8 - 1))
+ maxVal := float64(int64(1) << (1*8 - 1))
+ if fval != float64(int64(fval)) || fval < minVal ||
fval >= maxVal {
+ return &json.UnmarshalTypeError{
+ Value: v,
+ Type: reflect.TypeOf(int8(0)),
+ Offset: dec.InputOffset(),
+ }
+ }
+ f = int64(fval)
+ err = nil // Clear error after successful float parsing
+ }
if err != nil {
return &json.UnmarshalTypeError{
Value: v,
@@ -2158,7 +2454,31 @@ func (b *Int8Builder) UnmarshalOne(dec *json.Decoder)
error {
case float64:
b.Append(int8(v))
case json.Number:
+ // Try ParseInt first for direct integer strings, fall back to
ParseFloat for exponential notation
f, err := strconv.ParseInt(v.String(), 10, 1*8)
+ if err != nil {
+ // Could be exponential notation - try parsing as float
+ fval, ferr := strconv.ParseFloat(v.String(), 64)
+ if ferr != nil {
+ return &json.UnmarshalTypeError{
+ Value: v.String(),
+ Type: reflect.TypeOf(int8(0)),
+ Offset: dec.InputOffset(),
+ }
+ }
+ // Check if it's a whole number and in valid range
+ minVal := float64(int64(-1) << (1*8 - 1))
+ maxVal := float64(int64(1) << (1*8 - 1))
+ if fval != float64(int64(fval)) || fval < minVal ||
fval >= maxVal {
+ return &json.UnmarshalTypeError{
+ Value: v.String(),
+ Type: reflect.TypeOf(int8(0)),
+ Offset: dec.InputOffset(),
+ }
+ }
+ f = int64(fval)
+ err = nil // Clear error after successful float parsing
+ }
if err != nil {
return &json.UnmarshalTypeError{
Value: v.String(),
@@ -2387,7 +2707,29 @@ func (b *Uint8Builder) UnmarshalOne(dec *json.Decoder)
error {
b.AppendNull()
case string:
+ // Try ParseUint first for direct integer strings, fall back to
ParseFloat for exponential notation
f, err := strconv.ParseUint(v, 10, 1*8)
+ if err != nil {
+ // Could be exponential notation - try parsing as float
+ fval, ferr := strconv.ParseFloat(v, 64)
+ if ferr != nil {
+ return &json.UnmarshalTypeError{
+ Value: v,
+ Type: reflect.TypeOf(uint8(0)),
+ Offset: dec.InputOffset(),
+ }
+ }
+ // Check if it's a whole number and in valid range
+ if fval != float64(uint64(fval)) || fval < 0 || fval >
float64(^uint8(0)) {
+ return &json.UnmarshalTypeError{
+ Value: v,
+ Type: reflect.TypeOf(uint8(0)),
+ Offset: dec.InputOffset(),
+ }
+ }
+ f = uint64(fval)
+ err = nil // Clear error after successful float parsing
+ }
if err != nil {
return &json.UnmarshalTypeError{
Value: v,
@@ -2399,7 +2741,29 @@ func (b *Uint8Builder) UnmarshalOne(dec *json.Decoder)
error {
case float64:
b.Append(uint8(v))
case json.Number:
+ // Try ParseUint first for direct integer strings, fall back to
ParseFloat for exponential notation
f, err := strconv.ParseUint(v.String(), 10, 1*8)
+ if err != nil {
+ // Could be exponential notation - try parsing as float
+ fval, ferr := strconv.ParseFloat(v.String(), 64)
+ if ferr != nil {
+ return &json.UnmarshalTypeError{
+ Value: v.String(),
+ Type: reflect.TypeOf(uint8(0)),
+ Offset: dec.InputOffset(),
+ }
+ }
+ // Check if it's a whole number and in valid range
+ if fval != float64(uint64(fval)) || fval < 0 || fval >
float64(^uint8(0)) {
+ return &json.UnmarshalTypeError{
+ Value: v.String(),
+ Type: reflect.TypeOf(uint8(0)),
+ Offset: dec.InputOffset(),
+ }
+ }
+ f = uint64(fval)
+ err = nil // Clear error after successful float parsing
+ }
if err != nil {
return &json.UnmarshalTypeError{
Value: v.String(),
diff --git a/arrow/array/numericbuilder.gen.go.tmpl
b/arrow/array/numericbuilder.gen.go.tmpl
index 518b3d4c..5569b2eb 100644
--- a/arrow/array/numericbuilder.gen.go.tmpl
+++ b/arrow/array/numericbuilder.gen.go.tmpl
@@ -17,6 +17,9 @@
package array
import (
+ "reflect"
+ "strconv"
+
"github.com/apache/arrow-go/v18/arrow"
"github.com/apache/arrow-go/v18/arrow/bitutil"
"github.com/apache/arrow-go/v18/arrow/internal/debug"
@@ -378,9 +381,59 @@ func (b *{{.Name}}Builder) UnmarshalOne(dec *json.Decoder)
error {
{{if or (eq .Name "Float32") (eq .Name "Float64") -}}
f, err := strconv.ParseFloat(v, {{.Size}}*8)
{{else if eq (printf "%.1s" .Name) "U" -}}
+ // Try ParseUint first for direct integer strings, fall back to
ParseFloat for exponential notation
f, err := strconv.ParseUint(v, 10, {{.Size}}*8)
+ if err != nil {
+ // Could be exponential notation - try parsing as float
+ fval, ferr := strconv.ParseFloat(v, 64)
+ if ferr != nil {
+ return &json.UnmarshalTypeError{
+ Value: v,
+ Type: reflect.TypeOf({{.name}}(0)),
+ Offset: dec.InputOffset(),
+ }
+ }
+ // Check if it's a whole number and in valid range
+ if fval != float64(uint64(fval)) || fval < 0 || fval >
float64(^{{.name}}(0)) {
+ return &json.UnmarshalTypeError{
+ Value: v,
+ Type: reflect.TypeOf({{.name}}(0)),
+ Offset: dec.InputOffset(),
+ }
+ }
+ f = uint64(fval)
+ err = nil // Clear error after successful float parsing
+ }
{{else -}}
+ // Try ParseInt first for direct integer strings, fall back to
ParseFloat for exponential notation
f, err := strconv.ParseInt(v, 10, {{.Size}}*8)
+ if err != nil {
+ // Could be exponential notation - try parsing as float
+ fval, ferr := strconv.ParseFloat(v, 64)
+ if ferr != nil {
+ return &json.UnmarshalTypeError{
+ Value: v,
+ Type: reflect.TypeOf({{.name}}(0)),
+ Offset: dec.InputOffset(),
+ }
+ }
+ // Check if it's a whole number and in valid range
+ {{if eq .Size "8" -}}
+ if fval != float64(int64(fval)) || fval <
-9223372036854775808.0 || fval >= 9223372036854775808.0 {
+ {{else -}}
+ minVal := float64(int64(-1) << ({{.Size}}*8-1))
+ maxVal := float64(int64(1) << ({{.Size}}*8-1))
+ if fval != float64(int64(fval)) || fval < minVal ||
fval >= maxVal {
+ {{end -}}
+ return &json.UnmarshalTypeError{
+ Value: v,
+ Type: reflect.TypeOf({{.name}}(0)),
+ Offset: dec.InputOffset(),
+ }
+ }
+ f = int64(fval)
+ err = nil // Clear error after successful float parsing
+ }
{{end -}}
if err != nil {
return &json.UnmarshalTypeError{
@@ -396,9 +449,59 @@ func (b *{{.Name}}Builder) UnmarshalOne(dec *json.Decoder)
error {
{{if or (eq .Name "Float32") (eq .Name "Float64") -}}
f, err := strconv.ParseFloat(v.String(), {{.Size}}*8)
{{else if eq (printf "%.1s" .Name) "U" -}}
+ // Try ParseUint first for direct integer strings, fall back to
ParseFloat for exponential notation
f, err := strconv.ParseUint(v.String(), 10, {{.Size}}*8)
+ if err != nil {
+ // Could be exponential notation - try parsing as float
+ fval, ferr := strconv.ParseFloat(v.String(), 64)
+ if ferr != nil {
+ return &json.UnmarshalTypeError{
+ Value: v.String(),
+ Type: reflect.TypeOf({{.name}}(0)),
+ Offset: dec.InputOffset(),
+ }
+ }
+ // Check if it's a whole number and in valid range
+ if fval != float64(uint64(fval)) || fval < 0 || fval >
float64(^{{.name}}(0)) {
+ return &json.UnmarshalTypeError{
+ Value: v.String(),
+ Type: reflect.TypeOf({{.name}}(0)),
+ Offset: dec.InputOffset(),
+ }
+ }
+ f = uint64(fval)
+ err = nil // Clear error after successful float parsing
+ }
{{else -}}
+ // Try ParseInt first for direct integer strings, fall back to
ParseFloat for exponential notation
f, err := strconv.ParseInt(v.String(), 10, {{.Size}}*8)
+ if err != nil {
+ // Could be exponential notation - try parsing as float
+ fval, ferr := strconv.ParseFloat(v.String(), 64)
+ if ferr != nil {
+ return &json.UnmarshalTypeError{
+ Value: v.String(),
+ Type: reflect.TypeOf({{.name}}(0)),
+ Offset: dec.InputOffset(),
+ }
+ }
+ // Check if it's a whole number and in valid range
+ {{if eq .Size "8" -}}
+ if fval != float64(int64(fval)) || fval <
-9223372036854775808.0 || fval >= 9223372036854775808.0 {
+ {{else -}}
+ minVal := float64(int64(-1) << ({{.Size}}*8-1))
+ maxVal := float64(int64(1) << ({{.Size}}*8-1))
+ if fval != float64(int64(fval)) || fval < minVal ||
fval >= maxVal {
+ {{end -}}
+ return &json.UnmarshalTypeError{
+ Value: v.String(),
+ Type: reflect.TypeOf({{.name}}(0)),
+ Offset: dec.InputOffset(),
+ }
+ }
+ f = int64(fval)
+ err = nil // Clear error after successful float parsing
+ }
{{end -}}
if err != nil {
return &json.UnmarshalTypeError{