You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@arrow.apache.org by ze...@apache.org on 2026/05/19 03:10:51 UTC

(arrow-go) branch main updated: fix(arrow/array): preserve large integer precision in JSON decoding (#816)

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 fbc39237 fix(arrow/array): preserve large integer precision in JSON decoding (#816)
fbc39237 is described below

commit fbc3923778d2f20dcb59b9e4a3f69841dbfdc5a2
Author: Matt Topol <zo...@gmail.com>
AuthorDate: Mon May 18 23:10:45 2026 -0400

    fix(arrow/array): preserve large integer precision in JSON decoding (#816)
    
    ## Summary
    
    Fixes #804.
    
    JSON decoding in `arrow/array` silently corrupted integer values larger
    than 2^53 because Go's `encoding/json` decodes numeric tokens as
    `float64` by default, losing precision for very large integers. This PR
    makes `UseNumber` the unconditional default across `FromJSON`,
    `RecordFromJSON`, and `NewJSONReader` so full `int64` precision is
    preserved without requiring callers to opt in.
    
    ## Changes
    
    - **`RecordBuilder` gains `UnmarshalOne` and `Unmarshal`** so
    caller-configured `json.Decoder` options propagate through nested field
    decoding. `RecordFromJSON` now calls `bldr.UnmarshalOne(dec)` instead of
    `dec.Decode(bldr)`, avoiding the bytes-roundtrip through
    `json.Unmarshaler` that previously dropped the outer decoder
    configuration (root cause noted in [this
    comment](https://github.com/apache/arrow-go/issues/804#issuecomment-4454244067)).
    
    - **All builder `UnmarshalJSON([]byte)` methods now enable `UseNumber`**
    on their internal decoder so large integers also survive the standard
    `json.Unmarshal(data, &builder)` path.
    
    - **String-encoded integers now work for time-based types.** `Duration`,
    `Timestamp`, `Time32`, `Time64`, `Date32`, and `Date64` builders accept
    stringified integers (e.g. `"9223372036854775807"`) as a fallback before
    their format-specific parsers — addressing the second snippet from the
    issue. This matches how `Int64Builder` and the decimal builders already
    behaved.
    
    - **`WithUseNumber` and `WithUseNumberJSONReader` are deprecated**
    (marked with `Deprecated:` per pkg.go.dev convention) since `UseNumber`
    is now always enabled. Both options remain functional no-ops for
    backward compatibility.
    
    ## Test Plan
    
    15 new tests added in `arrow/array/util_test.go` and
    `arrow/array/json_reader_test.go` covering:
    
    - `RecordFromJSON` and `JSONReader` round-tripping `MaxInt64`/`MinInt64`
    with and without the deprecated options.
    - `RecordBuilder.UnmarshalJSON` and `UnmarshalOne` directly preserving
    large integers.
    - Stringified integer round-trip for `Duration`, `Timestamp`, `Time32`,
    `Time64`, `Date32`, `Date64`.
    - Regression: invalid duration strings (`"abc"`) still error;
    format-style duration strings (`"3h2m0.5s"`) still parse.
    
    Verified passing locally:
    
    - `./arrow/array/...` (full suite)
    - `./arrow/compute/...` (largest blast-radius consumer of
    `WithUseNumber` — 49 test sites)
    - `./arrow/ipc/...`, `./arrow/scalar/...`, `./arrow/extensions/...`,
    `./arrow/encoded/...`, `./arrow/util/...`
    - `./parquet/file/...` (the `./parquet/pqarrow/...` package fails
    identically on `main` due to a missing `PARQUET_TEST_DATA` env var —
    pre-existing, unrelated)
    
    All `lsp_diagnostics` clean across the 23 changed files.
    
    ## Behavior Change Notes
    
    The only observable behavior change for existing callers is: large
    integer values that previously decoded to a wrong `int64` (via float64
    round-trip) now decode to the correct value. Existing tests in
    `arrow/compute` continue to pass, including those that explicitly opt in
    via `WithUseNumber` and the ones that don't. No production callers in
    this repo break (verified across `flight`, `compute`, `parquet`, `ipc`).
---
 arrow/array/binarybuilder.go                |   2 +
 arrow/array/decimal.go                      |   1 +
 arrow/array/dictionary.go                   |   1 +
 arrow/array/encoded.go                      |   1 +
 arrow/array/fixed_size_list.go              |   1 +
 arrow/array/fixedsize_binarybuilder.go      |   1 +
 arrow/array/float16_builder.go              |   1 +
 arrow/array/interval.go                     |   3 +
 arrow/array/json_reader.go                  |   4 +-
 arrow/array/json_reader_test.go             |  25 ++
 arrow/array/list.go                         |   2 +
 arrow/array/map.go                          |   1 +
 arrow/array/null.go                         |   1 +
 arrow/array/numericbuilder.gen.go           | 469 ++++++++++++++++++++++------
 arrow/array/numericbuilder.gen.go.tmpl      | 199 ++++++++++--
 arrow/array/numericbuilder.gen_test.go      |  45 ++-
 arrow/array/numericbuilder.gen_test.go.tmpl |   6 +-
 arrow/array/record.go                       |  47 ++-
 arrow/array/string.go                       |   3 +
 arrow/array/struct.go                       |   1 +
 arrow/array/timestamp.go                    |  11 +
 arrow/array/union.go                        |   2 +
 arrow/array/util.go                         |  25 +-
 arrow/array/util_test.go                    | 261 ++++++++++++++++
 arrow/compute/arithmetic_test.go            |  30 +-
 arrow/compute/cast_test.go                  |  34 +-
 arrow/compute/scalar_compare_test.go        |  20 +-
 arrow/compute/scalar_set_lookup_test.go     |   2 +-
 arrow/compute/vector_selection_test.go      |  20 +-
 arrow/doc.go                                |   2 +-
 arrow/flight/flightsql/example/type_info.go |   2 +-
 31 files changed, 1007 insertions(+), 216 deletions(-)

diff --git a/arrow/array/binarybuilder.go b/arrow/array/binarybuilder.go
index 537ec933..5b881a84 100644
--- a/arrow/array/binarybuilder.go
+++ b/arrow/array/binarybuilder.go
@@ -375,6 +375,7 @@ func (b *BinaryBuilder) Unmarshal(dec *json.Decoder) error {
 
 func (b *BinaryBuilder) UnmarshalJSON(data []byte) error {
 	dec := json.NewDecoder(bytes.NewReader(data))
+	dec.UseNumber()
 	t, err := dec.Token()
 	if err != nil {
 		return err
@@ -669,6 +670,7 @@ func (b *BinaryViewBuilder) Unmarshal(dec *json.Decoder) error {
 
 func (b *BinaryViewBuilder) UnmarshalJSON(data []byte) error {
 	dec := json.NewDecoder(bytes.NewReader(data))
+	dec.UseNumber()
 	t, err := dec.Token()
 	if err != nil {
 		return err
diff --git a/arrow/array/decimal.go b/arrow/array/decimal.go
index 993242b9..84689c95 100644
--- a/arrow/array/decimal.go
+++ b/arrow/array/decimal.go
@@ -387,6 +387,7 @@ func (b *baseDecimalBuilder[T]) Unmarshal(dec *json.Decoder) error {
 
 func (b *baseDecimalBuilder[T]) UnmarshalJSON(data []byte) error {
 	dec := json.NewDecoder(bytes.NewReader(data))
+	dec.UseNumber()
 	t, err := dec.Token()
 	if err != nil {
 		return err
diff --git a/arrow/array/dictionary.go b/arrow/array/dictionary.go
index 8f9fe245..38a43f77 100644
--- a/arrow/array/dictionary.go
+++ b/arrow/array/dictionary.go
@@ -700,6 +700,7 @@ func (b *dictionaryBuilder) IsNull(i int) bool { return b.idxBuilder.IsNull(i) }
 
 func (b *dictionaryBuilder) UnmarshalJSON(data []byte) error {
 	dec := json.NewDecoder(bytes.NewReader(data))
+	dec.UseNumber()
 	t, err := dec.Token()
 	if err != nil {
 		return err
diff --git a/arrow/array/encoded.go b/arrow/array/encoded.go
index 85432a13..8d628ffc 100644
--- a/arrow/array/encoded.go
+++ b/arrow/array/encoded.go
@@ -520,6 +520,7 @@ func (b *RunEndEncodedBuilder) Unmarshal(dec *json.Decoder) error {
 // UnmarshalJSON can't be used in conjunction with AppendValueFromString (as it calls UnmarshalOne)
 func (b *RunEndEncodedBuilder) UnmarshalJSON(data []byte) error {
 	dec := json.NewDecoder(bytes.NewReader(data))
+	dec.UseNumber()
 	t, err := dec.Token()
 	if err != nil {
 		return err
diff --git a/arrow/array/fixed_size_list.go b/arrow/array/fixed_size_list.go
index 69cb67b2..d382ebe9 100644
--- a/arrow/array/fixed_size_list.go
+++ b/arrow/array/fixed_size_list.go
@@ -372,6 +372,7 @@ func (b *FixedSizeListBuilder) Unmarshal(dec *json.Decoder) error {
 
 func (b *FixedSizeListBuilder) UnmarshalJSON(data []byte) error {
 	dec := json.NewDecoder(bytes.NewReader(data))
+	dec.UseNumber()
 	t, err := dec.Token()
 	if err != nil {
 		return err
diff --git a/arrow/array/fixedsize_binarybuilder.go b/arrow/array/fixedsize_binarybuilder.go
index ca5a10c3..b6745be6 100644
--- a/arrow/array/fixedsize_binarybuilder.go
+++ b/arrow/array/fixedsize_binarybuilder.go
@@ -244,6 +244,7 @@ func (b *FixedSizeBinaryBuilder) Unmarshal(dec *json.Decoder) error {
 
 func (b *FixedSizeBinaryBuilder) UnmarshalJSON(data []byte) error {
 	dec := json.NewDecoder(bytes.NewReader(data))
+	dec.UseNumber()
 	t, err := dec.Token()
 	if err != nil {
 		return err
diff --git a/arrow/array/float16_builder.go b/arrow/array/float16_builder.go
index 60cc91b7..25e8f4a9 100644
--- a/arrow/array/float16_builder.go
+++ b/arrow/array/float16_builder.go
@@ -251,6 +251,7 @@ func (b *Float16Builder) Unmarshal(dec *json.Decoder) error {
 // be silently truncated.
 func (b *Float16Builder) UnmarshalJSON(data []byte) error {
 	dec := json.NewDecoder(bytes.NewReader(data))
+	dec.UseNumber()
 	t, err := dec.Token()
 	if err != nil {
 		return err
diff --git a/arrow/array/interval.go b/arrow/array/interval.go
index 2900a592..2c029c25 100644
--- a/arrow/array/interval.go
+++ b/arrow/array/interval.go
@@ -330,6 +330,7 @@ func (b *MonthIntervalBuilder) Unmarshal(dec *json.Decoder) error {
 // value that will be added to the builder.
 func (b *MonthIntervalBuilder) UnmarshalJSON(data []byte) error {
 	dec := json.NewDecoder(bytes.NewReader(data))
+	dec.UseNumber()
 	t, err := dec.Token()
 	if err != nil {
 		return err
@@ -631,6 +632,7 @@ func (b *DayTimeIntervalBuilder) Unmarshal(dec *json.Decoder) error {
 // with the values expected to be objects of the form {"days": #, "milliseconds": #}
 func (b *DayTimeIntervalBuilder) UnmarshalJSON(data []byte) error {
 	dec := json.NewDecoder(bytes.NewReader(data))
+	dec.UseNumber()
 	t, err := dec.Token()
 	if err != nil {
 		return err
@@ -936,6 +938,7 @@ func (b *MonthDayNanoIntervalBuilder) Unmarshal(dec *json.Decoder) error {
 // {"months": #, "days": #, "nanoseconds": #}
 func (b *MonthDayNanoIntervalBuilder) UnmarshalJSON(data []byte) error {
 	dec := json.NewDecoder(bytes.NewReader(data))
+	dec.UseNumber()
 	t, err := dec.Token()
 	if err != nil {
 		return err
diff --git a/arrow/array/json_reader.go b/arrow/array/json_reader.go
index d9ef5ed7..6aea688d 100644
--- a/arrow/array/json_reader.go
+++ b/arrow/array/json_reader.go
@@ -103,6 +103,8 @@ func NewJSONReader(r io.Reader, schema *arrow.Schema, opts ...Option) *JSONReade
 		o(rr)
 	}
 
+	rr.r.UseNumber()
+
 	if rr.mem == nil {
 		rr.mem = memory.DefaultAllocator
 	}
@@ -166,7 +168,7 @@ func (r *JSONReader) Next() bool {
 }
 
 func (r *JSONReader) readNext() bool {
-	r.err = r.r.Decode(r.bldr)
+	r.err = r.bldr.UnmarshalOne(r.r)
 	if r.err != nil {
 		r.done = true
 		if errors.Is(r.err, io.EOF) {
diff --git a/arrow/array/json_reader_test.go b/arrow/array/json_reader_test.go
index d309b0c3..fbf83428 100644
--- a/arrow/array/json_reader_test.go
+++ b/arrow/array/json_reader_test.go
@@ -27,6 +27,7 @@ import (
 	"github.com/apache/arrow-go/v18/arrow/memory"
 	"github.com/apache/arrow-go/v18/internal/json"
 	"github.com/stretchr/testify/assert"
+	"github.com/stretchr/testify/require"
 )
 
 const jsondata = `
@@ -246,6 +247,30 @@ func jsonArrayToNDJSON(data []byte) ([]byte, error) {
 	return ndjson.Bytes(), nil
 }
 
+func TestJSONReaderLargeInt64(t *testing.T) {
+	schema := arrow.NewSchema([]arrow.Field{
+		{Name: "a", Type: arrow.PrimitiveTypes.Int64},
+	}, nil)
+
+	mem := memory.NewCheckedAllocator(memory.NewGoAllocator())
+	defer mem.AssertSize(t, 0)
+
+	const ndjson = "{\"a\": 9223372036854775807}\n{\"a\": -9223372036854775808}\n"
+	rdr := array.NewJSONReader(strings.NewReader(ndjson), schema,
+		array.WithAllocator(mem), array.WithChunk(-1))
+	defer rdr.Release()
+
+	assert.True(t, rdr.Next())
+	rec := rdr.RecordBatch()
+	require.NotNil(t, rec)
+	assert.NoError(t, rdr.Err())
+	assert.EqualValues(t, 2, rec.NumRows())
+
+	col := rec.Column(0).(*array.Int64)
+	assert.EqualValues(t, int64(9223372036854775807), col.Value(0))
+	assert.EqualValues(t, int64(-9223372036854775808), col.Value(1))
+}
+
 func BenchmarkRecordFromJSON(b *testing.B) {
 	schema := arrow.NewSchema([]arrow.Field{
 		{Name: "id", Type: arrow.PrimitiveTypes.Int64},
diff --git a/arrow/array/list.go b/arrow/array/list.go
index d87cc266..d72887bb 100644
--- a/arrow/array/list.go
+++ b/arrow/array/list.go
@@ -627,6 +627,7 @@ func (b *baseListBuilder) Unmarshal(dec *json.Decoder) error {
 
 func (b *baseListBuilder) UnmarshalJSON(data []byte) error {
 	dec := json.NewDecoder(bytes.NewReader(data))
+	dec.UseNumber()
 	t, err := dec.Token()
 	if err != nil {
 		return err
@@ -1415,6 +1416,7 @@ func (b *baseListViewBuilder) Unmarshal(dec *json.Decoder) error {
 
 func (b *baseListViewBuilder) UnmarshalJSON(data []byte) error {
 	dec := json.NewDecoder(bytes.NewReader(data))
+	dec.UseNumber()
 	t, err := dec.Token()
 	if err != nil {
 		return err
diff --git a/arrow/array/map.go b/arrow/array/map.go
index da9a150b..71d4d138 100644
--- a/arrow/array/map.go
+++ b/arrow/array/map.go
@@ -342,6 +342,7 @@ func (b *MapBuilder) Unmarshal(dec *json.Decoder) error {
 
 func (b *MapBuilder) UnmarshalJSON(data []byte) error {
 	dec := json.NewDecoder(bytes.NewReader(data))
+	dec.UseNumber()
 	t, err := dec.Token()
 	if err != nil {
 		return err
diff --git a/arrow/array/null.go b/arrow/array/null.go
index 02ea12eb..8f8f5805 100644
--- a/arrow/array/null.go
+++ b/arrow/array/null.go
@@ -201,6 +201,7 @@ func (b *NullBuilder) Unmarshal(dec *json.Decoder) error {
 
 func (b *NullBuilder) UnmarshalJSON(data []byte) error {
 	dec := json.NewDecoder(bytes.NewReader(data))
+	dec.UseNumber()
 	t, err := dec.Token()
 	if err != nil {
 		return err
diff --git a/arrow/array/numericbuilder.gen.go b/arrow/array/numericbuilder.gen.go
index aa2f628d..b54f3e9d 100644
--- a/arrow/array/numericbuilder.gen.go
+++ b/arrow/array/numericbuilder.gen.go
@@ -21,6 +21,7 @@ package array
 import (
 	"bytes"
 	"fmt"
+	"math"
 	"reflect"
 	"strconv"
 	"strings"
@@ -218,10 +219,11 @@ 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
+		// Try ParseInt first for direct integer strings, fall back to ParseFloat for
+		// exponential notation. Reject NaN/Inf, then range-check before any int
+		// conversion to avoid undefined behavior on overflow or non-finite values.
 		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{
@@ -230,16 +232,33 @@ func (b *Int64Builder) UnmarshalOne(dec *json.Decoder) error {
 					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 {
+			if math.IsNaN(fval) || math.IsInf(fval, 0) || 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
+			// Beyond 2^53, float64 cannot represent every integer exactly, so
+			// exponent-form input like "9.007199254740993e15" may have been
+			// silently rounded by ParseFloat. Reject conservatively.
+			if math.Abs(fval) >= 9007199254740992 {
+				return &json.UnmarshalTypeError{
+					Value:  v,
+					Type:   reflect.TypeOf(int64(0)),
+					Offset: dec.InputOffset(),
+				}
+			}
+			truncated := int64(fval)
+			if fval != float64(truncated) {
+				return &json.UnmarshalTypeError{
+					Value:  v,
+					Type:   reflect.TypeOf(int64(0)),
+					Offset: dec.InputOffset(),
+				}
+			}
+			f = truncated
+			err = nil
 		}
 		if err != nil {
 			return &json.UnmarshalTypeError{
@@ -252,10 +271,13 @@ 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
+		// Try ParseInt first to preserve precision for integer values too large
+		// for float64. Fall back to ParseFloat to support exponential notation
+		// (e.g. "1e3"), but reject NaN/Inf, fractional, or out-of-range values
+		// so invalid JSON like 1.5 or 128 (for int8) is surfaced as
+		// UnmarshalTypeError rather than silently truncated or wrapped.
 		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{
@@ -264,16 +286,33 @@ func (b *Int64Builder) UnmarshalOne(dec *json.Decoder) error {
 					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 {
+			if math.IsNaN(fval) || math.IsInf(fval, 0) || fval < -9223372036854775808.0 || fval >= 9223372036854775808.0 {
+				return &json.UnmarshalTypeError{
+					Value:  v.String(),
+					Type:   reflect.TypeOf(int64(0)),
+					Offset: dec.InputOffset(),
+				}
+			}
+			// Beyond 2^53, float64 cannot represent every integer exactly, so
+			// exponent-form input like "9.007199254740993e15" may have been
+			// silently rounded by ParseFloat. Reject conservatively.
+			if math.Abs(fval) >= 9007199254740992 {
+				return &json.UnmarshalTypeError{
+					Value:  v.String(),
+					Type:   reflect.TypeOf(int64(0)),
+					Offset: dec.InputOffset(),
+				}
+			}
+			truncated := int64(fval)
+			if fval != float64(truncated) {
 				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
+			f = truncated
+			err = nil
 		}
 		if err != nil {
 			return &json.UnmarshalTypeError{
@@ -306,6 +345,7 @@ func (b *Int64Builder) Unmarshal(dec *json.Decoder) error {
 
 func (b *Int64Builder) UnmarshalJSON(data []byte) error {
 	dec := json.NewDecoder(bytes.NewReader(data))
+	dec.UseNumber()
 	t, err := dec.Token()
 	if err != nil {
 		return err
@@ -503,10 +543,11 @@ 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
+		// Try ParseUint first for direct integer strings, fall back to ParseFloat for
+		// exponential notation. Reject NaN/Inf, then range-check before any uint
+		// conversion to avoid undefined behavior on overflow or non-finite values.
 		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{
@@ -515,16 +556,33 @@ func (b *Uint64Builder) UnmarshalOne(dec *json.Decoder) error {
 					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)) {
+			if math.IsNaN(fval) || math.IsInf(fval, 0) || fval < 0 || fval >= float64(^uint64(0))+1 {
 				return &json.UnmarshalTypeError{
 					Value:  v,
 					Type:   reflect.TypeOf(uint64(0)),
 					Offset: dec.InputOffset(),
 				}
 			}
-			f = uint64(fval)
-			err = nil // Clear error after successful float parsing
+			// Beyond 2^53, float64 cannot represent every integer exactly, so
+			// exponent-form input like "9.007199254740993e15" may have been
+			// silently rounded by ParseFloat. Reject conservatively.
+			if math.Abs(fval) >= 9007199254740992 {
+				return &json.UnmarshalTypeError{
+					Value:  v,
+					Type:   reflect.TypeOf(uint64(0)),
+					Offset: dec.InputOffset(),
+				}
+			}
+			truncated := uint64(fval)
+			if fval != float64(truncated) {
+				return &json.UnmarshalTypeError{
+					Value:  v,
+					Type:   reflect.TypeOf(uint64(0)),
+					Offset: dec.InputOffset(),
+				}
+			}
+			f = truncated
+			err = nil
 		}
 		if err != nil {
 			return &json.UnmarshalTypeError{
@@ -537,10 +595,14 @@ 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
+		// Try ParseUint first to preserve precision for integer values too large
+		// for float64. Fall back to ParseFloat to support exponential notation
+		// (e.g. "1e3"), but reject NaN/Inf, fractional, or out-of-range values
+		// so invalid JSON is surfaced as UnmarshalTypeError rather than silently
+		// coerced. Range-check before any uint conversion to avoid undefined
+		// behavior on overflow or non-finite values.
 		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{
@@ -549,16 +611,36 @@ func (b *Uint64Builder) UnmarshalOne(dec *json.Decoder) error {
 					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)) {
+			// Boundary is max+1: for uint64, valid range is [0, max]; reject anything >= max+1.
+			// For uint64, float64(^uint64(0)) already rounds up to 2^64, so this naturally
+			// rejects the exact 2^64 boundary that the looser `>` check missed.
+			if math.IsNaN(fval) || math.IsInf(fval, 0) || fval < 0 || fval >= float64(^uint64(0))+1 {
+				return &json.UnmarshalTypeError{
+					Value:  v.String(),
+					Type:   reflect.TypeOf(uint64(0)),
+					Offset: dec.InputOffset(),
+				}
+			}
+			// Beyond 2^53, float64 cannot represent every integer exactly, so
+			// exponent-form input like "9.007199254740993e15" may have been
+			// silently rounded by ParseFloat. Reject conservatively.
+			if math.Abs(fval) >= 9007199254740992 {
+				return &json.UnmarshalTypeError{
+					Value:  v.String(),
+					Type:   reflect.TypeOf(uint64(0)),
+					Offset: dec.InputOffset(),
+				}
+			}
+			truncated := uint64(fval)
+			if fval != float64(truncated) {
 				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
+			f = truncated
+			err = nil
 		}
 		if err != nil {
 			return &json.UnmarshalTypeError{
@@ -591,6 +673,7 @@ func (b *Uint64Builder) Unmarshal(dec *json.Decoder) error {
 
 func (b *Uint64Builder) UnmarshalJSON(data []byte) error {
 	dec := json.NewDecoder(bytes.NewReader(data))
+	dec.UseNumber()
 	t, err := dec.Token()
 	if err != nil {
 		return err
@@ -832,6 +915,7 @@ func (b *Float64Builder) Unmarshal(dec *json.Decoder) error {
 
 func (b *Float64Builder) UnmarshalJSON(data []byte) error {
 	dec := json.NewDecoder(bytes.NewReader(data))
+	dec.UseNumber()
 	t, err := dec.Token()
 	if err != nil {
 		return err
@@ -1029,10 +1113,11 @@ 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
+		// Try ParseInt first for direct integer strings, fall back to ParseFloat for
+		// exponential notation. Reject NaN/Inf, then range-check before any int
+		// conversion to avoid undefined behavior on overflow or non-finite values.
 		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{
@@ -1041,18 +1126,25 @@ func (b *Int32Builder) UnmarshalOne(dec *json.Decoder) error {
 					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 {
+			if math.IsNaN(fval) || math.IsInf(fval, 0) || 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
+			truncated := int64(fval)
+			if fval != float64(truncated) {
+				return &json.UnmarshalTypeError{
+					Value:  v,
+					Type:   reflect.TypeOf(int32(0)),
+					Offset: dec.InputOffset(),
+				}
+			}
+			f = truncated
+			err = nil
 		}
 		if err != nil {
 			return &json.UnmarshalTypeError{
@@ -1065,10 +1157,13 @@ 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
+		// Try ParseInt first to preserve precision for integer values too large
+		// for float64. Fall back to ParseFloat to support exponential notation
+		// (e.g. "1e3"), but reject NaN/Inf, fractional, or out-of-range values
+		// so invalid JSON like 1.5 or 128 (for int8) is surfaced as
+		// UnmarshalTypeError rather than silently truncated or wrapped.
 		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{
@@ -1077,18 +1172,25 @@ func (b *Int32Builder) UnmarshalOne(dec *json.Decoder) error {
 					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 {
+			if math.IsNaN(fval) || math.IsInf(fval, 0) || fval < minVal || fval >= maxVal {
+				return &json.UnmarshalTypeError{
+					Value:  v.String(),
+					Type:   reflect.TypeOf(int32(0)),
+					Offset: dec.InputOffset(),
+				}
+			}
+			truncated := int64(fval)
+			if fval != float64(truncated) {
 				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
+			f = truncated
+			err = nil
 		}
 		if err != nil {
 			return &json.UnmarshalTypeError{
@@ -1121,6 +1223,7 @@ func (b *Int32Builder) Unmarshal(dec *json.Decoder) error {
 
 func (b *Int32Builder) UnmarshalJSON(data []byte) error {
 	dec := json.NewDecoder(bytes.NewReader(data))
+	dec.UseNumber()
 	t, err := dec.Token()
 	if err != nil {
 		return err
@@ -1318,10 +1421,11 @@ 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
+		// Try ParseUint first for direct integer strings, fall back to ParseFloat for
+		// exponential notation. Reject NaN/Inf, then range-check before any uint
+		// conversion to avoid undefined behavior on overflow or non-finite values.
 		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{
@@ -1330,16 +1434,23 @@ func (b *Uint32Builder) UnmarshalOne(dec *json.Decoder) error {
 					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)) {
+			if math.IsNaN(fval) || math.IsInf(fval, 0) || fval < 0 || fval >= float64(^uint32(0))+1 {
+				return &json.UnmarshalTypeError{
+					Value:  v,
+					Type:   reflect.TypeOf(uint32(0)),
+					Offset: dec.InputOffset(),
+				}
+			}
+			truncated := uint64(fval)
+			if fval != float64(truncated) {
 				return &json.UnmarshalTypeError{
 					Value:  v,
 					Type:   reflect.TypeOf(uint32(0)),
 					Offset: dec.InputOffset(),
 				}
 			}
-			f = uint64(fval)
-			err = nil // Clear error after successful float parsing
+			f = truncated
+			err = nil
 		}
 		if err != nil {
 			return &json.UnmarshalTypeError{
@@ -1352,10 +1463,14 @@ 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
+		// Try ParseUint first to preserve precision for integer values too large
+		// for float64. Fall back to ParseFloat to support exponential notation
+		// (e.g. "1e3"), but reject NaN/Inf, fractional, or out-of-range values
+		// so invalid JSON is surfaced as UnmarshalTypeError rather than silently
+		// coerced. Range-check before any uint conversion to avoid undefined
+		// behavior on overflow or non-finite values.
 		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{
@@ -1364,16 +1479,26 @@ func (b *Uint32Builder) UnmarshalOne(dec *json.Decoder) error {
 					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)) {
+			// Boundary is max+1: for uint32, valid range is [0, max]; reject anything >= max+1.
+			// For uint64, float64(^uint64(0)) already rounds up to 2^64, so this naturally
+			// rejects the exact 2^64 boundary that the looser `>` check missed.
+			if math.IsNaN(fval) || math.IsInf(fval, 0) || fval < 0 || fval >= float64(^uint32(0))+1 {
 				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
+			truncated := uint64(fval)
+			if fval != float64(truncated) {
+				return &json.UnmarshalTypeError{
+					Value:  v.String(),
+					Type:   reflect.TypeOf(uint32(0)),
+					Offset: dec.InputOffset(),
+				}
+			}
+			f = truncated
+			err = nil
 		}
 		if err != nil {
 			return &json.UnmarshalTypeError{
@@ -1406,6 +1531,7 @@ func (b *Uint32Builder) Unmarshal(dec *json.Decoder) error {
 
 func (b *Uint32Builder) UnmarshalJSON(data []byte) error {
 	dec := json.NewDecoder(bytes.NewReader(data))
+	dec.UseNumber()
 	t, err := dec.Token()
 	if err != nil {
 		return err
@@ -1647,6 +1773,7 @@ func (b *Float32Builder) Unmarshal(dec *json.Decoder) error {
 
 func (b *Float32Builder) UnmarshalJSON(data []byte) error {
 	dec := json.NewDecoder(bytes.NewReader(data))
+	dec.UseNumber()
 	t, err := dec.Token()
 	if err != nil {
 		return err
@@ -1844,10 +1971,11 @@ 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
+		// Try ParseInt first for direct integer strings, fall back to ParseFloat for
+		// exponential notation. Reject NaN/Inf, then range-check before any int
+		// conversion to avoid undefined behavior on overflow or non-finite values.
 		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{
@@ -1856,18 +1984,25 @@ func (b *Int16Builder) UnmarshalOne(dec *json.Decoder) error {
 					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 {
+			if math.IsNaN(fval) || math.IsInf(fval, 0) || fval < minVal || fval >= maxVal {
+				return &json.UnmarshalTypeError{
+					Value:  v,
+					Type:   reflect.TypeOf(int16(0)),
+					Offset: dec.InputOffset(),
+				}
+			}
+			truncated := int64(fval)
+			if fval != float64(truncated) {
 				return &json.UnmarshalTypeError{
 					Value:  v,
 					Type:   reflect.TypeOf(int16(0)),
 					Offset: dec.InputOffset(),
 				}
 			}
-			f = int64(fval)
-			err = nil // Clear error after successful float parsing
+			f = truncated
+			err = nil
 		}
 		if err != nil {
 			return &json.UnmarshalTypeError{
@@ -1880,10 +2015,13 @@ 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
+		// Try ParseInt first to preserve precision for integer values too large
+		// for float64. Fall back to ParseFloat to support exponential notation
+		// (e.g. "1e3"), but reject NaN/Inf, fractional, or out-of-range values
+		// so invalid JSON like 1.5 or 128 (for int8) is surfaced as
+		// UnmarshalTypeError rather than silently truncated or wrapped.
 		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{
@@ -1892,18 +2030,25 @@ func (b *Int16Builder) UnmarshalOne(dec *json.Decoder) error {
 					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 {
+			if math.IsNaN(fval) || math.IsInf(fval, 0) || fval < minVal || fval >= maxVal {
+				return &json.UnmarshalTypeError{
+					Value:  v.String(),
+					Type:   reflect.TypeOf(int16(0)),
+					Offset: dec.InputOffset(),
+				}
+			}
+			truncated := int64(fval)
+			if fval != float64(truncated) {
 				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
+			f = truncated
+			err = nil
 		}
 		if err != nil {
 			return &json.UnmarshalTypeError{
@@ -1936,6 +2081,7 @@ func (b *Int16Builder) Unmarshal(dec *json.Decoder) error {
 
 func (b *Int16Builder) UnmarshalJSON(data []byte) error {
 	dec := json.NewDecoder(bytes.NewReader(data))
+	dec.UseNumber()
 	t, err := dec.Token()
 	if err != nil {
 		return err
@@ -2133,10 +2279,11 @@ 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
+		// Try ParseUint first for direct integer strings, fall back to ParseFloat for
+		// exponential notation. Reject NaN/Inf, then range-check before any uint
+		// conversion to avoid undefined behavior on overflow or non-finite values.
 		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{
@@ -2145,16 +2292,23 @@ func (b *Uint16Builder) UnmarshalOne(dec *json.Decoder) error {
 					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)) {
+			if math.IsNaN(fval) || math.IsInf(fval, 0) || fval < 0 || fval >= float64(^uint16(0))+1 {
 				return &json.UnmarshalTypeError{
 					Value:  v,
 					Type:   reflect.TypeOf(uint16(0)),
 					Offset: dec.InputOffset(),
 				}
 			}
-			f = uint64(fval)
-			err = nil // Clear error after successful float parsing
+			truncated := uint64(fval)
+			if fval != float64(truncated) {
+				return &json.UnmarshalTypeError{
+					Value:  v,
+					Type:   reflect.TypeOf(uint16(0)),
+					Offset: dec.InputOffset(),
+				}
+			}
+			f = truncated
+			err = nil
 		}
 		if err != nil {
 			return &json.UnmarshalTypeError{
@@ -2167,10 +2321,14 @@ 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
+		// Try ParseUint first to preserve precision for integer values too large
+		// for float64. Fall back to ParseFloat to support exponential notation
+		// (e.g. "1e3"), but reject NaN/Inf, fractional, or out-of-range values
+		// so invalid JSON is surfaced as UnmarshalTypeError rather than silently
+		// coerced. Range-check before any uint conversion to avoid undefined
+		// behavior on overflow or non-finite values.
 		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{
@@ -2179,16 +2337,26 @@ func (b *Uint16Builder) UnmarshalOne(dec *json.Decoder) error {
 					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)) {
+			// Boundary is max+1: for uint16, valid range is [0, max]; reject anything >= max+1.
+			// For uint64, float64(^uint64(0)) already rounds up to 2^64, so this naturally
+			// rejects the exact 2^64 boundary that the looser `>` check missed.
+			if math.IsNaN(fval) || math.IsInf(fval, 0) || fval < 0 || fval >= float64(^uint16(0))+1 {
+				return &json.UnmarshalTypeError{
+					Value:  v.String(),
+					Type:   reflect.TypeOf(uint16(0)),
+					Offset: dec.InputOffset(),
+				}
+			}
+			truncated := uint64(fval)
+			if fval != float64(truncated) {
 				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
+			f = truncated
+			err = nil
 		}
 		if err != nil {
 			return &json.UnmarshalTypeError{
@@ -2221,6 +2389,7 @@ func (b *Uint16Builder) Unmarshal(dec *json.Decoder) error {
 
 func (b *Uint16Builder) UnmarshalJSON(data []byte) error {
 	dec := json.NewDecoder(bytes.NewReader(data))
+	dec.UseNumber()
 	t, err := dec.Token()
 	if err != nil {
 		return err
@@ -2418,10 +2587,11 @@ 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
+		// Try ParseInt first for direct integer strings, fall back to ParseFloat for
+		// exponential notation. Reject NaN/Inf, then range-check before any int
+		// conversion to avoid undefined behavior on overflow or non-finite values.
 		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{
@@ -2430,18 +2600,25 @@ func (b *Int8Builder) UnmarshalOne(dec *json.Decoder) error {
 					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 {
+			if math.IsNaN(fval) || math.IsInf(fval, 0) || fval < minVal || fval >= maxVal {
+				return &json.UnmarshalTypeError{
+					Value:  v,
+					Type:   reflect.TypeOf(int8(0)),
+					Offset: dec.InputOffset(),
+				}
+			}
+			truncated := int64(fval)
+			if fval != float64(truncated) {
 				return &json.UnmarshalTypeError{
 					Value:  v,
 					Type:   reflect.TypeOf(int8(0)),
 					Offset: dec.InputOffset(),
 				}
 			}
-			f = int64(fval)
-			err = nil // Clear error after successful float parsing
+			f = truncated
+			err = nil
 		}
 		if err != nil {
 			return &json.UnmarshalTypeError{
@@ -2454,10 +2631,13 @@ 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
+		// Try ParseInt first to preserve precision for integer values too large
+		// for float64. Fall back to ParseFloat to support exponential notation
+		// (e.g. "1e3"), but reject NaN/Inf, fractional, or out-of-range values
+		// so invalid JSON like 1.5 or 128 (for int8) is surfaced as
+		// UnmarshalTypeError rather than silently truncated or wrapped.
 		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{
@@ -2466,18 +2646,25 @@ func (b *Int8Builder) UnmarshalOne(dec *json.Decoder) error {
 					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 {
+			if math.IsNaN(fval) || math.IsInf(fval, 0) || 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
+			truncated := int64(fval)
+			if fval != float64(truncated) {
+				return &json.UnmarshalTypeError{
+					Value:  v.String(),
+					Type:   reflect.TypeOf(int8(0)),
+					Offset: dec.InputOffset(),
+				}
+			}
+			f = truncated
+			err = nil
 		}
 		if err != nil {
 			return &json.UnmarshalTypeError{
@@ -2510,6 +2697,7 @@ func (b *Int8Builder) Unmarshal(dec *json.Decoder) error {
 
 func (b *Int8Builder) UnmarshalJSON(data []byte) error {
 	dec := json.NewDecoder(bytes.NewReader(data))
+	dec.UseNumber()
 	t, err := dec.Token()
 	if err != nil {
 		return err
@@ -2707,10 +2895,11 @@ 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
+		// Try ParseUint first for direct integer strings, fall back to ParseFloat for
+		// exponential notation. Reject NaN/Inf, then range-check before any uint
+		// conversion to avoid undefined behavior on overflow or non-finite values.
 		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{
@@ -2719,16 +2908,23 @@ func (b *Uint8Builder) UnmarshalOne(dec *json.Decoder) error {
 					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)) {
+			if math.IsNaN(fval) || math.IsInf(fval, 0) || fval < 0 || fval >= float64(^uint8(0))+1 {
+				return &json.UnmarshalTypeError{
+					Value:  v,
+					Type:   reflect.TypeOf(uint8(0)),
+					Offset: dec.InputOffset(),
+				}
+			}
+			truncated := uint64(fval)
+			if fval != float64(truncated) {
 				return &json.UnmarshalTypeError{
 					Value:  v,
 					Type:   reflect.TypeOf(uint8(0)),
 					Offset: dec.InputOffset(),
 				}
 			}
-			f = uint64(fval)
-			err = nil // Clear error after successful float parsing
+			f = truncated
+			err = nil
 		}
 		if err != nil {
 			return &json.UnmarshalTypeError{
@@ -2741,10 +2937,14 @@ 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
+		// Try ParseUint first to preserve precision for integer values too large
+		// for float64. Fall back to ParseFloat to support exponential notation
+		// (e.g. "1e3"), but reject NaN/Inf, fractional, or out-of-range values
+		// so invalid JSON is surfaced as UnmarshalTypeError rather than silently
+		// coerced. Range-check before any uint conversion to avoid undefined
+		// behavior on overflow or non-finite values.
 		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{
@@ -2753,16 +2953,26 @@ func (b *Uint8Builder) UnmarshalOne(dec *json.Decoder) error {
 					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)) {
+			// Boundary is max+1: for uint8, valid range is [0, max]; reject anything >= max+1.
+			// For uint64, float64(^uint64(0)) already rounds up to 2^64, so this naturally
+			// rejects the exact 2^64 boundary that the looser `>` check missed.
+			if math.IsNaN(fval) || math.IsInf(fval, 0) || fval < 0 || fval >= float64(^uint8(0))+1 {
 				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
+			truncated := uint64(fval)
+			if fval != float64(truncated) {
+				return &json.UnmarshalTypeError{
+					Value:  v.String(),
+					Type:   reflect.TypeOf(uint8(0)),
+					Offset: dec.InputOffset(),
+				}
+			}
+			f = truncated
+			err = nil
 		}
 		if err != nil {
 			return &json.UnmarshalTypeError{
@@ -2795,6 +3005,7 @@ func (b *Uint8Builder) Unmarshal(dec *json.Decoder) error {
 
 func (b *Uint8Builder) UnmarshalJSON(data []byte) error {
 	dec := json.NewDecoder(bytes.NewReader(data))
+	dec.UseNumber()
 	t, err := dec.Token()
 	if err != nil {
 		return err
@@ -2973,6 +3184,10 @@ func (b *Time32Builder) AppendValueFromString(s string) error {
 		b.AppendNull()
 		return nil
 	}
+	if v, parseErr := strconv.ParseInt(s, 10, 32); parseErr == nil {
+		b.Append(arrow.Time32(v))
+		return nil
+	}
 	val, err := arrow.Time32FromString(s, b.dtype.Unit)
 	if err != nil {
 		b.AppendNull()
@@ -2992,6 +3207,10 @@ func (b *Time32Builder) UnmarshalOne(dec *json.Decoder) error {
 	case nil:
 		b.AppendNull()
 	case string:
+		if i, parseErr := strconv.ParseInt(v, 10, 4*8); parseErr == nil {
+			b.Append(arrow.Time32(i))
+			break
+		}
 		tm, err := arrow.Time32FromString(v, b.dtype.Unit)
 		if err != nil {
 			return &json.UnmarshalTypeError{
@@ -3011,6 +3230,15 @@ func (b *Time32Builder) UnmarshalOne(dec *json.Decoder) error {
 				Offset: dec.InputOffset(),
 			}
 		}
+		// arrow.Time32 is int32-backed; reject values outside int32 range
+		// to avoid silent wrap-around.
+		if n < -2147483648 || n > 2147483647 {
+			return &json.UnmarshalTypeError{
+				Value:  v.String(),
+				Type:   reflect.TypeOf(arrow.Time32(0)),
+				Offset: dec.InputOffset(),
+			}
+		}
 		b.Append(arrow.Time32(n))
 	case float64:
 		b.Append(arrow.Time32(v))
@@ -3037,6 +3265,7 @@ func (b *Time32Builder) Unmarshal(dec *json.Decoder) error {
 
 func (b *Time32Builder) UnmarshalJSON(data []byte) error {
 	dec := json.NewDecoder(bytes.NewReader(data))
+	dec.UseNumber()
 	t, err := dec.Token()
 	if err != nil {
 		return err
@@ -3215,6 +3444,10 @@ func (b *Time64Builder) AppendValueFromString(s string) error {
 		b.AppendNull()
 		return nil
 	}
+	if v, parseErr := strconv.ParseInt(s, 10, 64); parseErr == nil {
+		b.Append(arrow.Time64(v))
+		return nil
+	}
 	val, err := arrow.Time64FromString(s, b.dtype.Unit)
 	if err != nil {
 		b.AppendNull()
@@ -3234,6 +3467,10 @@ func (b *Time64Builder) UnmarshalOne(dec *json.Decoder) error {
 	case nil:
 		b.AppendNull()
 	case string:
+		if i, parseErr := strconv.ParseInt(v, 10, 8*8); parseErr == nil {
+			b.Append(arrow.Time64(i))
+			break
+		}
 		tm, err := arrow.Time64FromString(v, b.dtype.Unit)
 		if err != nil {
 			return &json.UnmarshalTypeError{
@@ -3279,6 +3516,7 @@ func (b *Time64Builder) Unmarshal(dec *json.Decoder) error {
 
 func (b *Time64Builder) UnmarshalJSON(data []byte) error {
 	dec := json.NewDecoder(bytes.NewReader(data))
+	dec.UseNumber()
 	t, err := dec.Token()
 	if err != nil {
 		return err
@@ -3456,6 +3694,10 @@ func (b *Date32Builder) AppendValueFromString(s string) error {
 		b.AppendNull()
 		return nil
 	}
+	if v, parseErr := strconv.ParseInt(s, 10, 32); parseErr == nil {
+		b.Append(arrow.Date32(v))
+		return nil
+	}
 	tm, err := time.Parse("2006-01-02", s)
 	if err != nil {
 		b.AppendNull()
@@ -3475,6 +3717,10 @@ func (b *Date32Builder) UnmarshalOne(dec *json.Decoder) error {
 	case nil:
 		b.AppendNull()
 	case string:
+		if i, parseErr := strconv.ParseInt(v, 10, 4*8); parseErr == nil {
+			b.Append(arrow.Date32(i))
+			break
+		}
 		tm, err := time.Parse("2006-01-02", v)
 		if err != nil {
 			return &json.UnmarshalTypeError{
@@ -3494,6 +3740,15 @@ func (b *Date32Builder) UnmarshalOne(dec *json.Decoder) error {
 				Offset: dec.InputOffset(),
 			}
 		}
+		// arrow.Date32 is int32-backed; reject values outside int32 range
+		// to avoid silent wrap-around.
+		if n < -2147483648 || n > 2147483647 {
+			return &json.UnmarshalTypeError{
+				Value:  v.String(),
+				Type:   reflect.TypeOf(arrow.Date32(0)),
+				Offset: dec.InputOffset(),
+			}
+		}
 		b.Append(arrow.Date32(n))
 	case float64:
 		b.Append(arrow.Date32(v))
@@ -3520,6 +3775,7 @@ func (b *Date32Builder) Unmarshal(dec *json.Decoder) error {
 
 func (b *Date32Builder) UnmarshalJSON(data []byte) error {
 	dec := json.NewDecoder(bytes.NewReader(data))
+	dec.UseNumber()
 	t, err := dec.Token()
 	if err != nil {
 		return err
@@ -3697,6 +3953,10 @@ func (b *Date64Builder) AppendValueFromString(s string) error {
 		b.AppendNull()
 		return nil
 	}
+	if v, parseErr := strconv.ParseInt(s, 10, 64); parseErr == nil {
+		b.Append(arrow.Date64(v))
+		return nil
+	}
 	tm, err := time.Parse("2006-01-02", s)
 	if err != nil {
 		b.AppendNull()
@@ -3716,6 +3976,10 @@ func (b *Date64Builder) UnmarshalOne(dec *json.Decoder) error {
 	case nil:
 		b.AppendNull()
 	case string:
+		if i, parseErr := strconv.ParseInt(v, 10, 8*8); parseErr == nil {
+			b.Append(arrow.Date64(i))
+			break
+		}
 		tm, err := time.Parse("2006-01-02", v)
 		if err != nil {
 			return &json.UnmarshalTypeError{
@@ -3761,6 +4025,7 @@ func (b *Date64Builder) Unmarshal(dec *json.Decoder) error {
 
 func (b *Date64Builder) UnmarshalJSON(data []byte) error {
 	dec := json.NewDecoder(bytes.NewReader(data))
+	dec.UseNumber()
 	t, err := dec.Token()
 	if err != nil {
 		return err
@@ -3939,6 +4204,10 @@ func (b *DurationBuilder) AppendValueFromString(s string) error {
 		b.AppendNull()
 		return nil
 	}
+	if v, parseErr := strconv.ParseInt(s, 10, 64); parseErr == nil {
+		b.Append(arrow.Duration(v))
+		return nil
+	}
 	dur, err := time.ParseDuration(s)
 	if err != nil {
 		return err
@@ -3970,6 +4239,13 @@ func (b *DurationBuilder) UnmarshalOne(dec *json.Decoder) error {
 	case float64:
 		b.Append(arrow.Duration(v))
 	case string:
+		// raw integer strings (e.g. "9223372036854775807") - useful when the
+		// caller serializes large integers as strings to bypass JSON number
+		// precision limits
+		if i, parseErr := strconv.ParseInt(v, 10, 64); parseErr == nil {
+			b.Append(arrow.Duration(i))
+			break
+		}
 		// be flexible for specifying durations by accepting forms like
 		// 3h2m0.5s regardless of the unit and converting it to the proper
 		// precision.
@@ -4026,6 +4302,7 @@ func (b *DurationBuilder) Unmarshal(dec *json.Decoder) error {
 
 func (b *DurationBuilder) UnmarshalJSON(data []byte) error {
 	dec := json.NewDecoder(bytes.NewReader(data))
+	dec.UseNumber()
 	t, err := dec.Token()
 	if err != nil {
 		return err
diff --git a/arrow/array/numericbuilder.gen.go.tmpl b/arrow/array/numericbuilder.gen.go.tmpl
index 5569b2eb..1f7d433e 100644
--- a/arrow/array/numericbuilder.gen.go.tmpl
+++ b/arrow/array/numericbuilder.gen.go.tmpl
@@ -17,6 +17,7 @@
 package array
 
 import (
+	"math"
 	"reflect"
 	"strconv"
 
@@ -213,6 +214,10 @@ func (b *{{.Name}}Builder) AppendValueFromString(s string) error {
 		return nil
 	}
   {{if or (eq .Name "Date32") -}}
+  	if v, parseErr := strconv.ParseInt(s, 10, 32); parseErr == nil {
+  		b.Append(arrow.Date32(v))
+  		return nil
+  	}
   	tm, err := time.Parse("2006-01-02", s)
     if err != nil {
       b.AppendNull()
@@ -220,6 +225,10 @@ func (b *{{.Name}}Builder) AppendValueFromString(s string) error {
     }
     b.Append(arrow.Date32FromTime(tm))
   {{else if or (eq .Name "Date64") -}}
+  	if v, parseErr := strconv.ParseInt(s, 10, 64); parseErr == nil {
+  		b.Append(arrow.Date64(v))
+  		return nil
+  	}
   	tm, err := time.Parse("2006-01-02", s)
     if err != nil {
       b.AppendNull()
@@ -227,6 +236,10 @@ func (b *{{.Name}}Builder) AppendValueFromString(s string) error {
     }
     b.Append(arrow.Date64FromTime(tm))
   {{else if or (eq .Name "Time32") -}}
+  	if v, parseErr := strconv.ParseInt(s, 10, 32); parseErr == nil {
+  		b.Append(arrow.Time32(v))
+  		return nil
+  	}
   	val, err := arrow.Time32FromString(s, b.dtype.Unit)
     if err != nil {
       b.AppendNull()
@@ -234,6 +247,10 @@ func (b *{{.Name}}Builder) AppendValueFromString(s string) error {
     }
     b.Append(val)
   {{else if or (eq .Name "Time64") -}}
+  	if v, parseErr := strconv.ParseInt(s, 10, 64); parseErr == nil {
+  		b.Append(arrow.Time64(v))
+  		return nil
+  	}
     val, err := arrow.Time64FromString(s, b.dtype.Unit)
     if err != nil {
       b.AppendNull()
@@ -241,6 +258,10 @@ func (b *{{.Name}}Builder) AppendValueFromString(s string) error {
     }
     b.Append(val)
   {{else if (eq .Name "Duration") -}}
+	if v, parseErr := strconv.ParseInt(s, 10, 64); parseErr == nil {
+		b.Append(arrow.Duration(v))
+		return nil
+	}
 	dur, err := time.ParseDuration(s)
 	if err != nil {
 		return err
@@ -283,6 +304,10 @@ func (b *{{.Name}}Builder) UnmarshalOne(dec *json.Decoder) error {
 		b.AppendNull()
 {{if or (eq .Name "Date32") (eq .Name "Date64") -}}
 	case string:
+		if i, parseErr := strconv.ParseInt(v, 10, {{.Size}}*8); parseErr == nil {
+			b.Append({{.QualifiedType}}(i))
+			break
+		}
 		tm, err := time.Parse("2006-01-02", v)
 		if err != nil {
 			return &json.UnmarshalTypeError{
@@ -302,11 +327,26 @@ func (b *{{.Name}}Builder) UnmarshalOne(dec *json.Decoder) error {
 				Offset: dec.InputOffset(),
 			}
 		}
+		{{if eq .Size "4" -}}
+		// {{.QualifiedType}} is int32-backed; reject values outside int32 range
+		// to avoid silent wrap-around.
+		if n < -2147483648 || n > 2147483647 {
+			return &json.UnmarshalTypeError{
+				Value: v.String(),
+				Type: reflect.TypeOf({{.QualifiedType}}(0)),
+				Offset: dec.InputOffset(),
+			}
+		}
+		{{end -}}
 		b.Append({{.QualifiedType}}(n))
 	case float64:
 		b.Append({{.QualifiedType}}(v))
 {{else if or (eq .Name "Time32") (eq .Name "Time64") -}}
 	case string:
+		if i, parseErr := strconv.ParseInt(v, 10, {{.Size}}*8); parseErr == nil {
+			b.Append({{.QualifiedType}}(i))
+			break
+		}
 		tm, err := {{.QualifiedType}}FromString(v, b.dtype.Unit)
 		if err != nil {
 			return &json.UnmarshalTypeError{
@@ -326,6 +366,17 @@ func (b *{{.Name}}Builder) UnmarshalOne(dec *json.Decoder) error {
 				Offset: dec.InputOffset(),
 			}
 		}
+		{{if eq .Size "4" -}}
+		// {{.QualifiedType}} is int32-backed; reject values outside int32 range
+		// to avoid silent wrap-around.
+		if n < -2147483648 || n > 2147483647 {
+			return &json.UnmarshalTypeError{
+				Value: v.String(),
+				Type: reflect.TypeOf({{.QualifiedType}}(0)),
+				Offset: dec.InputOffset(),
+			}
+		}
+		{{end -}}
 		b.Append({{.QualifiedType}}(n))
 	case float64:
 		b.Append({{.QualifiedType}}(v))
@@ -343,6 +394,13 @@ func (b *{{.Name}}Builder) UnmarshalOne(dec *json.Decoder) error {
 	case float64:
 		b.Append({{.QualifiedType}}(v))
 	case string:
+		// raw integer strings (e.g. "9223372036854775807") - useful when the
+		// caller serializes large integers as strings to bypass JSON number
+		// precision limits
+		if i, parseErr := strconv.ParseInt(v, 10, 64); parseErr == nil {
+			b.Append(arrow.Duration(i))
+			break
+		}
 		// be flexible for specifying durations by accepting forms like
 		// 3h2m0.5s regardless of the unit and converting it to the proper
 		// precision.
@@ -381,10 +439,11 @@ 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
+		// Try ParseUint first for direct integer strings, fall back to ParseFloat for
+		// exponential notation. Reject NaN/Inf, then range-check before any uint
+		// conversion to avoid undefined behavior on overflow or non-finite values.
 		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{
@@ -393,22 +452,42 @@ func (b *{{.Name}}Builder) UnmarshalOne(dec *json.Decoder) error {
 					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)) {
+			if math.IsNaN(fval) || math.IsInf(fval, 0) || fval < 0 || fval >= float64(^{{.name}}(0))+1 {
 				return &json.UnmarshalTypeError{
 					Value: v,
 					Type: reflect.TypeOf({{.name}}(0)),
 					Offset: dec.InputOffset(),
 				}
 			}
-			f = uint64(fval)
-			err = nil // Clear error after successful float parsing
+			{{if eq .Size "8" -}}
+			// Beyond 2^53, float64 cannot represent every integer exactly, so
+			// exponent-form input like "9.007199254740993e15" may have been
+			// silently rounded by ParseFloat. Reject conservatively.
+			if math.Abs(fval) >= 9007199254740992 {
+				return &json.UnmarshalTypeError{
+					Value: v,
+					Type: reflect.TypeOf({{.name}}(0)),
+					Offset: dec.InputOffset(),
+				}
+			}
+			{{end -}}
+			truncated := uint64(fval)
+			if fval != float64(truncated) {
+				return &json.UnmarshalTypeError{
+					Value: v,
+					Type: reflect.TypeOf({{.name}}(0)),
+					Offset: dec.InputOffset(),
+				}
+			}
+			f = truncated
+			err = nil
 		}
 {{else -}}
-		// Try ParseInt first for direct integer strings, fall back to ParseFloat for exponential notation
+		// Try ParseInt first for direct integer strings, fall back to ParseFloat for
+		// exponential notation. Reject NaN/Inf, then range-check before any int
+		// conversion to avoid undefined behavior on overflow or non-finite values.
 		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{
@@ -417,13 +496,12 @@ func (b *{{.Name}}Builder) UnmarshalOne(dec *json.Decoder) error {
 					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 {
+			if math.IsNaN(fval) || math.IsInf(fval, 0) || 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 {
+			if math.IsNaN(fval) || math.IsInf(fval, 0) || fval < minVal || fval >= maxVal {
 			{{end -}}
 				return &json.UnmarshalTypeError{
 					Value: v,
@@ -431,8 +509,28 @@ func (b *{{.Name}}Builder) UnmarshalOne(dec *json.Decoder) error {
 					Offset: dec.InputOffset(),
 				}
 			}
-			f = int64(fval)
-			err = nil // Clear error after successful float parsing
+			{{if eq .Size "8" -}}
+			// Beyond 2^53, float64 cannot represent every integer exactly, so
+			// exponent-form input like "9.007199254740993e15" may have been
+			// silently rounded by ParseFloat. Reject conservatively.
+			if math.Abs(fval) >= 9007199254740992 {
+				return &json.UnmarshalTypeError{
+					Value: v,
+					Type: reflect.TypeOf({{.name}}(0)),
+					Offset: dec.InputOffset(),
+				}
+			}
+			{{end -}}
+			truncated := int64(fval)
+			if fval != float64(truncated) {
+				return &json.UnmarshalTypeError{
+					Value: v,
+					Type: reflect.TypeOf({{.name}}(0)),
+					Offset: dec.InputOffset(),
+				}
+			}
+			f = truncated
+			err = nil
 		}
 {{end -}}
 		if err != nil {
@@ -449,10 +547,14 @@ 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
+		// Try ParseUint first to preserve precision for integer values too large
+		// for float64. Fall back to ParseFloat to support exponential notation
+		// (e.g. "1e3"), but reject NaN/Inf, fractional, or out-of-range values
+		// so invalid JSON is surfaced as UnmarshalTypeError rather than silently
+		// coerced. Range-check before any uint conversion to avoid undefined
+		// behavior on overflow or non-finite values.
 		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{
@@ -461,22 +563,47 @@ func (b *{{.Name}}Builder) UnmarshalOne(dec *json.Decoder) error {
 					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)) {
+			// Boundary is max+1: for {{.name}}, valid range is [0, max]; reject anything >= max+1.
+			// For uint64, float64(^uint64(0)) already rounds up to 2^64, so this naturally
+			// rejects the exact 2^64 boundary that the looser `>` check missed.
+			if math.IsNaN(fval) || math.IsInf(fval, 0) || fval < 0 || fval >= float64(^{{.name}}(0))+1 {
+				return &json.UnmarshalTypeError{
+					Value: v.String(),
+					Type: reflect.TypeOf({{.name}}(0)),
+					Offset: dec.InputOffset(),
+				}
+			}
+			{{if eq .Size "8" -}}
+			// Beyond 2^53, float64 cannot represent every integer exactly, so
+			// exponent-form input like "9.007199254740993e15" may have been
+			// silently rounded by ParseFloat. Reject conservatively.
+			if math.Abs(fval) >= 9007199254740992 {
+				return &json.UnmarshalTypeError{
+					Value: v.String(),
+					Type: reflect.TypeOf({{.name}}(0)),
+					Offset: dec.InputOffset(),
+				}
+			}
+			{{end -}}
+			truncated := uint64(fval)
+			if fval != float64(truncated) {
 				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
+			f = truncated
+			err = nil
 		}
 {{else -}}
-		// Try ParseInt first for direct integer strings, fall back to ParseFloat for exponential notation
+		// Try ParseInt first to preserve precision for integer values too large
+		// for float64. Fall back to ParseFloat to support exponential notation
+		// (e.g. "1e3"), but reject NaN/Inf, fractional, or out-of-range values
+		// so invalid JSON like 1.5 or 128 (for int8) is surfaced as
+		// UnmarshalTypeError rather than silently truncated or wrapped.
 		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{
@@ -485,22 +612,41 @@ func (b *{{.Name}}Builder) UnmarshalOne(dec *json.Decoder) error {
 					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 {
+			if math.IsNaN(fval) || math.IsInf(fval, 0) || 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 {
+			if math.IsNaN(fval) || math.IsInf(fval, 0) || fval < minVal || fval >= maxVal {
+			{{end -}}
+				return &json.UnmarshalTypeError{
+					Value: v.String(),
+					Type: reflect.TypeOf({{.name}}(0)),
+					Offset: dec.InputOffset(),
+				}
+			}
+			{{if eq .Size "8" -}}
+			// Beyond 2^53, float64 cannot represent every integer exactly, so
+			// exponent-form input like "9.007199254740993e15" may have been
+			// silently rounded by ParseFloat. Reject conservatively.
+			if math.Abs(fval) >= 9007199254740992 {
+				return &json.UnmarshalTypeError{
+					Value: v.String(),
+					Type: reflect.TypeOf({{.name}}(0)),
+					Offset: dec.InputOffset(),
+				}
+			}
 			{{end -}}
+			truncated := int64(fval)
+			if fval != float64(truncated) {
 				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
+			f = truncated
+			err = nil
 		}
 {{end -}}
 		if err != nil {
@@ -534,6 +680,7 @@ func (b *{{.Name}}Builder) Unmarshal(dec *json.Decoder) error {
 
 func (b *{{.Name}}Builder) UnmarshalJSON(data []byte) error {
 	dec := json.NewDecoder(bytes.NewReader(data))
+	dec.UseNumber()
 	t, err := dec.Token()
 	if err != nil {
 		return err
diff --git a/arrow/array/numericbuilder.gen_test.go b/arrow/array/numericbuilder.gen_test.go
index 1336815b..2ca56329 100644
--- a/arrow/array/numericbuilder.gen_test.go
+++ b/arrow/array/numericbuilder.gen_test.go
@@ -237,7 +237,7 @@ func TestInt64BuilderUnmarshalJSON(t *testing.T) {
 	bldr := array.NewInt64Builder(mem)
 	defer bldr.Release()
 
-	jsonstr := `[0, 1, null, 2.3, -11]`
+	jsonstr := `[0, 1, null, 2, 100]`
 
 	err := bldr.UnmarshalJSON([]byte(jsonstr))
 	assert.NoError(t, err)
@@ -251,6 +251,7 @@ func TestInt64BuilderUnmarshalJSON(t *testing.T) {
 	assert.Equal(t, int64(1), int64(arr.Value(1)))
 	assert.True(t, arr.IsNull(2))
 	assert.Equal(t, int64(2), int64(arr.Value(3)))
+	assert.Equal(t, int64(100), int64(arr.Value(4)))
 	assert.Equal(t, int64(5), int64(arr.Len()))
 }
 
@@ -463,7 +464,7 @@ func TestUint64BuilderUnmarshalJSON(t *testing.T) {
 	bldr := array.NewUint64Builder(mem)
 	defer bldr.Release()
 
-	jsonstr := `[0, 1, null, 2.3, -11]`
+	jsonstr := `[0, 1, null, 2, 100]`
 
 	err := bldr.UnmarshalJSON([]byte(jsonstr))
 	assert.NoError(t, err)
@@ -477,6 +478,7 @@ func TestUint64BuilderUnmarshalJSON(t *testing.T) {
 	assert.Equal(t, int64(1), int64(arr.Value(1)))
 	assert.True(t, arr.IsNull(2))
 	assert.Equal(t, int64(2), int64(arr.Value(3)))
+	assert.Equal(t, int64(100), int64(arr.Value(4)))
 	assert.Equal(t, int64(5), int64(arr.Len()))
 }
 
@@ -689,7 +691,7 @@ func TestFloat64BuilderUnmarshalJSON(t *testing.T) {
 	bldr := array.NewFloat64Builder(mem)
 	defer bldr.Release()
 
-	jsonstr := `[0, 1, "+Inf", 2, 3, "NaN", "NaN", 4, 5, "-Inf"]`
+	jsonstr := `[0, 1, "+Inf", 2.5, 3, "NaN", "NaN", 4, 5, "-Inf"]`
 
 	err := bldr.UnmarshalJSON([]byte(jsonstr))
 	assert.NoError(t, err)
@@ -702,6 +704,7 @@ func TestFloat64BuilderUnmarshalJSON(t *testing.T) {
 	assert.False(t, math.IsInf(float64(arr.Value(0)), 0), arr.Value(0))
 	assert.True(t, math.IsInf(float64(arr.Value(2)), 1), arr.Value(2))
 	assert.True(t, math.IsNaN(float64(arr.Value(5))), arr.Value(5))
+	assert.Equal(t, float64(2.5), arr.Value(3))
 }
 
 func TestInt32StringRoundTrip(t *testing.T) {
@@ -913,7 +916,7 @@ func TestInt32BuilderUnmarshalJSON(t *testing.T) {
 	bldr := array.NewInt32Builder(mem)
 	defer bldr.Release()
 
-	jsonstr := `[0, 1, null, 2.3, -11]`
+	jsonstr := `[0, 1, null, 2, 100]`
 
 	err := bldr.UnmarshalJSON([]byte(jsonstr))
 	assert.NoError(t, err)
@@ -927,6 +930,7 @@ func TestInt32BuilderUnmarshalJSON(t *testing.T) {
 	assert.Equal(t, int64(1), int64(arr.Value(1)))
 	assert.True(t, arr.IsNull(2))
 	assert.Equal(t, int64(2), int64(arr.Value(3)))
+	assert.Equal(t, int64(100), int64(arr.Value(4)))
 	assert.Equal(t, int64(5), int64(arr.Len()))
 }
 
@@ -1139,7 +1143,7 @@ func TestUint32BuilderUnmarshalJSON(t *testing.T) {
 	bldr := array.NewUint32Builder(mem)
 	defer bldr.Release()
 
-	jsonstr := `[0, 1, null, 2.3, -11]`
+	jsonstr := `[0, 1, null, 2, 100]`
 
 	err := bldr.UnmarshalJSON([]byte(jsonstr))
 	assert.NoError(t, err)
@@ -1153,6 +1157,7 @@ func TestUint32BuilderUnmarshalJSON(t *testing.T) {
 	assert.Equal(t, int64(1), int64(arr.Value(1)))
 	assert.True(t, arr.IsNull(2))
 	assert.Equal(t, int64(2), int64(arr.Value(3)))
+	assert.Equal(t, int64(100), int64(arr.Value(4)))
 	assert.Equal(t, int64(5), int64(arr.Len()))
 }
 
@@ -1365,7 +1370,7 @@ func TestFloat32BuilderUnmarshalJSON(t *testing.T) {
 	bldr := array.NewFloat32Builder(mem)
 	defer bldr.Release()
 
-	jsonstr := `[0, 1, "+Inf", 2, 3, "NaN", "NaN", 4, 5, "-Inf"]`
+	jsonstr := `[0, 1, "+Inf", 2.5, 3, "NaN", "NaN", 4, 5, "-Inf"]`
 
 	err := bldr.UnmarshalJSON([]byte(jsonstr))
 	assert.NoError(t, err)
@@ -1378,6 +1383,7 @@ func TestFloat32BuilderUnmarshalJSON(t *testing.T) {
 	assert.False(t, math.IsInf(float64(arr.Value(0)), 0), arr.Value(0))
 	assert.True(t, math.IsInf(float64(arr.Value(2)), 1), arr.Value(2))
 	assert.True(t, math.IsNaN(float64(arr.Value(5))), arr.Value(5))
+	assert.Equal(t, float32(2.5), arr.Value(3))
 }
 
 func TestInt16StringRoundTrip(t *testing.T) {
@@ -1589,7 +1595,7 @@ func TestInt16BuilderUnmarshalJSON(t *testing.T) {
 	bldr := array.NewInt16Builder(mem)
 	defer bldr.Release()
 
-	jsonstr := `[0, 1, null, 2.3, -11]`
+	jsonstr := `[0, 1, null, 2, 100]`
 
 	err := bldr.UnmarshalJSON([]byte(jsonstr))
 	assert.NoError(t, err)
@@ -1603,6 +1609,7 @@ func TestInt16BuilderUnmarshalJSON(t *testing.T) {
 	assert.Equal(t, int64(1), int64(arr.Value(1)))
 	assert.True(t, arr.IsNull(2))
 	assert.Equal(t, int64(2), int64(arr.Value(3)))
+	assert.Equal(t, int64(100), int64(arr.Value(4)))
 	assert.Equal(t, int64(5), int64(arr.Len()))
 }
 
@@ -1815,7 +1822,7 @@ func TestUint16BuilderUnmarshalJSON(t *testing.T) {
 	bldr := array.NewUint16Builder(mem)
 	defer bldr.Release()
 
-	jsonstr := `[0, 1, null, 2.3, -11]`
+	jsonstr := `[0, 1, null, 2, 100]`
 
 	err := bldr.UnmarshalJSON([]byte(jsonstr))
 	assert.NoError(t, err)
@@ -1829,6 +1836,7 @@ func TestUint16BuilderUnmarshalJSON(t *testing.T) {
 	assert.Equal(t, int64(1), int64(arr.Value(1)))
 	assert.True(t, arr.IsNull(2))
 	assert.Equal(t, int64(2), int64(arr.Value(3)))
+	assert.Equal(t, int64(100), int64(arr.Value(4)))
 	assert.Equal(t, int64(5), int64(arr.Len()))
 }
 
@@ -2041,7 +2049,7 @@ func TestInt8BuilderUnmarshalJSON(t *testing.T) {
 	bldr := array.NewInt8Builder(mem)
 	defer bldr.Release()
 
-	jsonstr := `[0, 1, null, 2.3, -11]`
+	jsonstr := `[0, 1, null, 2, 100]`
 
 	err := bldr.UnmarshalJSON([]byte(jsonstr))
 	assert.NoError(t, err)
@@ -2055,6 +2063,7 @@ func TestInt8BuilderUnmarshalJSON(t *testing.T) {
 	assert.Equal(t, int64(1), int64(arr.Value(1)))
 	assert.True(t, arr.IsNull(2))
 	assert.Equal(t, int64(2), int64(arr.Value(3)))
+	assert.Equal(t, int64(100), int64(arr.Value(4)))
 	assert.Equal(t, int64(5), int64(arr.Len()))
 }
 
@@ -2267,7 +2276,7 @@ func TestUint8BuilderUnmarshalJSON(t *testing.T) {
 	bldr := array.NewUint8Builder(mem)
 	defer bldr.Release()
 
-	jsonstr := `[0, 1, null, 2.3, -11]`
+	jsonstr := `[0, 1, null, 2, 100]`
 
 	err := bldr.UnmarshalJSON([]byte(jsonstr))
 	assert.NoError(t, err)
@@ -2281,6 +2290,7 @@ func TestUint8BuilderUnmarshalJSON(t *testing.T) {
 	assert.Equal(t, int64(1), int64(arr.Value(1)))
 	assert.True(t, arr.IsNull(2))
 	assert.Equal(t, int64(2), int64(arr.Value(3)))
+	assert.Equal(t, int64(100), int64(arr.Value(4)))
 	assert.Equal(t, int64(5), int64(arr.Len()))
 }
 
@@ -2499,7 +2509,7 @@ func TestTime32BuilderUnmarshalJSON(t *testing.T) {
 	bldr := array.NewTime32Builder(mem, dtype)
 	defer bldr.Release()
 
-	jsonstr := `[0, 1, null, 2.3, -11]`
+	jsonstr := `[0, 1, null, 2, 100]`
 
 	err := bldr.UnmarshalJSON([]byte(jsonstr))
 	assert.NoError(t, err)
@@ -2513,6 +2523,7 @@ func TestTime32BuilderUnmarshalJSON(t *testing.T) {
 	assert.Equal(t, int64(1), int64(arr.Value(1)))
 	assert.True(t, arr.IsNull(2))
 	assert.Equal(t, int64(2), int64(arr.Value(3)))
+	assert.Equal(t, int64(100), int64(arr.Value(4)))
 	assert.Equal(t, int64(5), int64(arr.Len()))
 }
 
@@ -2731,7 +2742,7 @@ func TestTime64BuilderUnmarshalJSON(t *testing.T) {
 	bldr := array.NewTime64Builder(mem, dtype)
 	defer bldr.Release()
 
-	jsonstr := `[0, 1, null, 2.3, -11]`
+	jsonstr := `[0, 1, null, 2, 100]`
 
 	err := bldr.UnmarshalJSON([]byte(jsonstr))
 	assert.NoError(t, err)
@@ -2745,6 +2756,7 @@ func TestTime64BuilderUnmarshalJSON(t *testing.T) {
 	assert.Equal(t, int64(1), int64(arr.Value(1)))
 	assert.True(t, arr.IsNull(2))
 	assert.Equal(t, int64(2), int64(arr.Value(3)))
+	assert.Equal(t, int64(100), int64(arr.Value(4)))
 	assert.Equal(t, int64(5), int64(arr.Len()))
 }
 
@@ -2957,7 +2969,7 @@ func TestDate32BuilderUnmarshalJSON(t *testing.T) {
 	bldr := array.NewDate32Builder(mem)
 	defer bldr.Release()
 
-	jsonstr := `[0, 1, null, 2.3, -11]`
+	jsonstr := `[0, 1, null, 2, 100]`
 
 	err := bldr.UnmarshalJSON([]byte(jsonstr))
 	assert.NoError(t, err)
@@ -2971,6 +2983,7 @@ func TestDate32BuilderUnmarshalJSON(t *testing.T) {
 	assert.Equal(t, int64(1), int64(arr.Value(1)))
 	assert.True(t, arr.IsNull(2))
 	assert.Equal(t, int64(2), int64(arr.Value(3)))
+	assert.Equal(t, int64(100), int64(arr.Value(4)))
 	assert.Equal(t, int64(5), int64(arr.Len()))
 }
 
@@ -3190,7 +3203,7 @@ func TestDate64BuilderUnmarshalJSON(t *testing.T) {
 	bldr := array.NewDate64Builder(mem)
 	defer bldr.Release()
 
-	jsonstr := `[0, 1, null, 2.3, -11]`
+	jsonstr := `[0, 1, null, 2, 100]`
 
 	err := bldr.UnmarshalJSON([]byte(jsonstr))
 	assert.NoError(t, err)
@@ -3204,6 +3217,7 @@ func TestDate64BuilderUnmarshalJSON(t *testing.T) {
 	assert.Equal(t, int64(1), int64(arr.Value(1)))
 	assert.True(t, arr.IsNull(2))
 	assert.Equal(t, int64(2), int64(arr.Value(3)))
+	assert.Equal(t, int64(100), int64(arr.Value(4)))
 	assert.Equal(t, int64(5), int64(arr.Len()))
 }
 
@@ -3422,7 +3436,7 @@ func TestDurationBuilderUnmarshalJSON(t *testing.T) {
 	bldr := array.NewDurationBuilder(mem, dtype)
 	defer bldr.Release()
 
-	jsonstr := `[0, 1, null, 2.3, -11]`
+	jsonstr := `[0, 1, null, 2, 100]`
 
 	err := bldr.UnmarshalJSON([]byte(jsonstr))
 	assert.NoError(t, err)
@@ -3436,5 +3450,6 @@ func TestDurationBuilderUnmarshalJSON(t *testing.T) {
 	assert.Equal(t, int64(1), int64(arr.Value(1)))
 	assert.True(t, arr.IsNull(2))
 	assert.Equal(t, int64(2), int64(arr.Value(3)))
+	assert.Equal(t, int64(100), int64(arr.Value(4)))
 	assert.Equal(t, int64(5), int64(arr.Len()))
 }
diff --git a/arrow/array/numericbuilder.gen_test.go.tmpl b/arrow/array/numericbuilder.gen_test.go.tmpl
index 86cc74a5..07d5fc8a 100644
--- a/arrow/array/numericbuilder.gen_test.go.tmpl
+++ b/arrow/array/numericbuilder.gen_test.go.tmpl
@@ -287,7 +287,7 @@ func Test{{.Name}}BuilderUnmarshalJSON(t *testing.T) {
 
 
 {{ if or (eq .Name "Float64") (eq .Name "Float32") -}}
-	jsonstr := `[0, 1, "+Inf", 2, 3, "NaN", "NaN", 4, 5, "-Inf"]`
+	jsonstr := `[0, 1, "+Inf", 2.5, 3, "NaN", "NaN", 4, 5, "-Inf"]`
 
 	err := bldr.UnmarshalJSON([]byte(jsonstr))
 	assert.NoError(t, err)
@@ -300,8 +300,9 @@ func Test{{.Name}}BuilderUnmarshalJSON(t *testing.T) {
 	assert.False(t, math.IsInf(float64(arr.Value(0)), 0), arr.Value(0))
 	assert.True(t, math.IsInf(float64(arr.Value(2)), 1), arr.Value(2))
 	assert.True(t, math.IsNaN(float64(arr.Value(5))), arr.Value(5))
+	assert.Equal(t, {{ .Type }}(2.5), arr.Value(3))
 {{else}}
-	jsonstr := `[0, 1, null, 2.3, -11]`
+	jsonstr := `[0, 1, null, 2, 100]`
 
 	err := bldr.UnmarshalJSON([]byte(jsonstr))
 	assert.NoError(t, err)
@@ -315,6 +316,7 @@ func Test{{.Name}}BuilderUnmarshalJSON(t *testing.T) {
 	assert.Equal(t, int64(1),  int64(arr.Value(1)))
 	assert.True(t, arr.IsNull(2))
 	assert.Equal(t, int64(2),  int64(arr.Value(3)))
+	assert.Equal(t, int64(100), int64(arr.Value(4)))
 	assert.Equal(t, int64(5),  int64(arr.Len()))
 {{end -}}
 }
diff --git a/arrow/array/record.go b/arrow/array/record.go
index 0aaf771b..17bfb783 100644
--- a/arrow/array/record.go
+++ b/arrow/array/record.go
@@ -384,12 +384,15 @@ func (b *RecordBuilder) NewRecord() arrow.Record {
 	return b.NewRecordBatch()
 }
 
-// UnmarshalJSON for record builder will read in a single object and add the values
-// to each field in the recordbuilder, missing fields will get a null and unexpected
-// keys will be ignored. If reading in an array of records as a single batch, then use
-// a structbuilder and use RecordFromStruct.
-func (b *RecordBuilder) UnmarshalJSON(data []byte) error {
-	dec := json.NewDecoder(bytes.NewReader(data))
+// UnmarshalOne reads one row (a JSON object) from the supplied decoder and
+// appends a value to each field in the RecordBuilder. Missing fields are
+// appended as nulls and unrecognized keys are silently ignored.
+//
+// Unlike UnmarshalJSON, this method receives an already-configured
+// json.Decoder, so options such as UseNumber set by the caller are honored
+// for nested field decoding. This is critical for preserving large integer
+// values (>2^53) that cannot be represented exactly as float64.
+func (b *RecordBuilder) UnmarshalOne(dec *json.Decoder) error {
 	// should start with a '{'
 	t, err := dec.Token()
 	if err != nil {
@@ -427,6 +430,11 @@ func (b *RecordBuilder) UnmarshalJSON(data []byte) error {
 		}
 	}
 
+	// consume the closing '}'
+	if _, err := dec.Token(); err != nil {
+		return err
+	}
+
 	for i := 0; i < b.schema.NumFields(); i++ {
 		if !keylist[b.schema.Field(i).Name] {
 			b.fields[i].AppendNull()
@@ -435,6 +443,33 @@ func (b *RecordBuilder) UnmarshalJSON(data []byte) error {
 	return nil
 }
 
+// Unmarshal reads multiple rows from the decoder, calling UnmarshalOne in a
+// loop until dec.More() reports there are no more values. Like UnmarshalOne,
+// this honors decoder configuration such as UseNumber set by the caller.
+func (b *RecordBuilder) Unmarshal(dec *json.Decoder) error {
+	for dec.More() {
+		if err := b.UnmarshalOne(dec); err != nil {
+			return err
+		}
+	}
+	return nil
+}
+
+// UnmarshalJSON for record builder will read in a single object and add the values
+// to each field in the recordbuilder, missing fields will get a null and unexpected
+// keys will be ignored. If reading in an array of records as a single batch, then use
+// a structbuilder and use RecordFromStruct.
+//
+// UseNumber is enabled on the internal decoder so that integer values too large
+// to be represented exactly as float64 (e.g. values beyond 2^53) are preserved.
+// Callers who need full control over decoder configuration should use
+// UnmarshalOne with a pre-configured json.Decoder instead.
+func (b *RecordBuilder) UnmarshalJSON(data []byte) error {
+	dec := json.NewDecoder(bytes.NewReader(data))
+	dec.UseNumber()
+	return b.UnmarshalOne(dec)
+}
+
 type iterReader struct {
 	refCount atomic.Int64
 
diff --git a/arrow/array/string.go b/arrow/array/string.go
index 60323e37..35b412f2 100644
--- a/arrow/array/string.go
+++ b/arrow/array/string.go
@@ -629,6 +629,7 @@ func (b *StringBuilder) Unmarshal(dec *json.Decoder) error {
 
 func (b *StringBuilder) UnmarshalJSON(data []byte) error {
 	dec := json.NewDecoder(bytes.NewReader(data))
+	dec.UseNumber()
 	t, err := dec.Token()
 	if err != nil {
 		return err
@@ -722,6 +723,7 @@ func (b *LargeStringBuilder) Unmarshal(dec *json.Decoder) error {
 
 func (b *LargeStringBuilder) UnmarshalJSON(data []byte) error {
 	dec := json.NewDecoder(bytes.NewReader(data))
+	dec.UseNumber()
 	t, err := dec.Token()
 	if err != nil {
 		return err
@@ -788,6 +790,7 @@ func (b *StringViewBuilder) Unmarshal(dec *json.Decoder) error {
 
 func (b *StringViewBuilder) UnmarshalJSON(data []byte) error {
 	dec := json.NewDecoder(bytes.NewReader(data))
+	dec.UseNumber()
 	t, err := dec.Token()
 	if err != nil {
 		return err
diff --git a/arrow/array/struct.go b/arrow/array/struct.go
index 6883712c..7df6d8d4 100644
--- a/arrow/array/struct.go
+++ b/arrow/array/struct.go
@@ -520,6 +520,7 @@ func (b *StructBuilder) Unmarshal(dec *json.Decoder) error {
 
 func (b *StructBuilder) UnmarshalJSON(data []byte) error {
 	dec := json.NewDecoder(bytes.NewReader(data))
+	dec.UseNumber()
 	t, err := dec.Token()
 	if err != nil {
 		return err
diff --git a/arrow/array/timestamp.go b/arrow/array/timestamp.go
index d0b5b062..5ac0ee6c 100644
--- a/arrow/array/timestamp.go
+++ b/arrow/array/timestamp.go
@@ -20,6 +20,7 @@ import (
 	"bytes"
 	"fmt"
 	"reflect"
+	"strconv"
 	"strings"
 	"time"
 
@@ -315,6 +316,11 @@ func (b *TimestampBuilder) AppendValueFromString(s string) error {
 		return nil
 	}
 
+	if i, parseErr := strconv.ParseInt(s, 10, 64); parseErr == nil {
+		b.Append(arrow.Timestamp(i))
+		return nil
+	}
+
 	loc, err := b.dtype.GetZone()
 	if err != nil {
 		return err
@@ -339,6 +345,10 @@ func (b *TimestampBuilder) UnmarshalOne(dec *json.Decoder) error {
 	case nil:
 		b.AppendNull()
 	case string:
+		if i, parseErr := strconv.ParseInt(v, 10, 64); parseErr == nil {
+			b.Append(arrow.Timestamp(i))
+			break
+		}
 		loc, _ := b.dtype.GetZone()
 		tm, _, err := arrow.TimestampFromStringInLocation(v, b.dtype.Unit, loc)
 		if err != nil {
@@ -385,6 +395,7 @@ func (b *TimestampBuilder) Unmarshal(dec *json.Decoder) error {
 
 func (b *TimestampBuilder) UnmarshalJSON(data []byte) error {
 	dec := json.NewDecoder(bytes.NewReader(data))
+	dec.UseNumber()
 	t, err := dec.Token()
 	if err != nil {
 		return err
diff --git a/arrow/array/union.go b/arrow/array/union.go
index 9c13af05..beaaf4e1 100644
--- a/arrow/array/union.go
+++ b/arrow/array/union.go
@@ -998,6 +998,7 @@ func (b *SparseUnionBuilder) NewSparseUnionArray() (a *SparseUnion) {
 
 func (b *SparseUnionBuilder) UnmarshalJSON(data []byte) (err error) {
 	dec := json.NewDecoder(bytes.NewReader(data))
+	dec.UseNumber()
 	t, err := dec.Token()
 	if err != nil {
 		return err
@@ -1257,6 +1258,7 @@ func (b *DenseUnionBuilder) NewDenseUnionArray() (a *DenseUnion) {
 
 func (b *DenseUnionBuilder) UnmarshalJSON(data []byte) (err error) {
 	dec := json.NewDecoder(bytes.NewReader(data))
+	dec.UseNumber()
 	t, err := dec.Token()
 	if err != nil {
 		return err
diff --git a/arrow/array/util.go b/arrow/array/util.go
index 5cbcbeab..136ed353 100644
--- a/arrow/array/util.go
+++ b/arrow/array/util.go
@@ -61,10 +61,13 @@ func WithStartOffset(off int64) FromJSONOption {
 	}
 }
 
-// WithUseNumber enables the 'UseNumber' option on the json decoder, using
-// the json.Number type instead of assuming float64 for numbers. This is critical
-// if you have numbers that are larger than what can fit into the 53 bits of
-// an IEEE float64 mantissa and want to preserve its value.
+// WithUseNumber previously enabled the 'UseNumber' option on the json decoder.
+// As of issue #804, FromJSON, RecordFromJSON, and TableFromJSON unconditionally
+// enable UseNumber so that integer values too large to fit in float64 (i.e.
+// beyond 2^53) are preserved without silent corruption. This option is now a
+// no-op and is retained for backward compatibility.
+//
+// Deprecated: UseNumber is now always enabled; this option has no effect.
 func WithUseNumber() FromJSONOption {
 	return func(c *fromJSONCfg) {
 		c.useNumber = true
@@ -143,16 +146,13 @@ func FromJSON(mem memory.Allocator, dt arrow.DataType, r io.Reader, opts ...From
 	defer bldr.Release()
 
 	dec := json.NewDecoder(r)
+	dec.UseNumber()
 	defer func() {
 		if errors.Is(err, io.EOF) {
 			err = fmt.Errorf("failed parsing json: %w", io.ErrUnexpectedEOF)
 		}
 	}()
 
-	if cfg.useNumber {
-		dec.UseNumber()
-	}
-
 	if !cfg.multiDocument {
 		t, err := dec.Token()
 		if err != nil {
@@ -237,9 +237,7 @@ func RecordFromJSON(mem memory.Allocator, schema *arrow.Schema, r io.Reader, opt
 	defer bldr.Release()
 
 	dec := json.NewDecoder(r)
-	if cfg.useNumber {
-		dec.UseNumber()
-	}
+	dec.UseNumber()
 
 	if !cfg.multiDocument {
 		t, err := dec.Token()
@@ -251,7 +249,7 @@ func RecordFromJSON(mem memory.Allocator, schema *arrow.Schema, r io.Reader, opt
 		}
 
 		for dec.More() {
-			if err := dec.Decode(bldr); err != nil {
+			if err := bldr.UnmarshalOne(dec); err != nil {
 				return nil, dec.InputOffset(), fmt.Errorf("failed to decode json: %w", err)
 			}
 		}
@@ -265,8 +263,7 @@ func RecordFromJSON(mem memory.Allocator, schema *arrow.Schema, r io.Reader, opt
 	}
 
 	for {
-		err := dec.Decode(bldr)
-		if err != nil {
+		if err := bldr.UnmarshalOne(dec); err != nil {
 			if errors.Is(err, io.EOF) {
 				break
 			}
diff --git a/arrow/array/util_test.go b/arrow/array/util_test.go
index fb837871..eb3de6a8 100644
--- a/arrow/array/util_test.go
+++ b/arrow/array/util_test.go
@@ -543,3 +543,264 @@ func TestRecordBuilderUnmarshalJSONExtraFields(t *testing.T) {
 
 	assert.Truef(t, array.RecordEqual(rec1, rec2), "expected: %s\nactual: %s", rec1, rec2)
 }
+
+func TestRecordFromJSONLargeInt64Default(t *testing.T) {
+	mem := memory.NewCheckedAllocator(memory.DefaultAllocator)
+	defer mem.AssertSize(t, 0)
+
+	schema := arrow.NewSchema([]arrow.Field{
+		{Name: "a", Type: arrow.PrimitiveTypes.Int64},
+	}, nil)
+
+	const data = `[{"a": 9223372036854775807}, {"a": -9223372036854775808}]`
+	batch, _, err := array.RecordFromJSON(mem, schema, strings.NewReader(data))
+	require.NoError(t, err)
+	require.NotNil(t, batch)
+	defer batch.Release()
+
+	col := batch.Column(0).(*array.Int64)
+	assert.EqualValues(t, int64(9223372036854775807), col.Value(0))
+	assert.EqualValues(t, int64(-9223372036854775808), col.Value(1))
+}
+
+func TestRecordFromJSONLargeInt64WithUseNumber(t *testing.T) {
+	mem := memory.NewCheckedAllocator(memory.DefaultAllocator)
+	defer mem.AssertSize(t, 0)
+
+	schema := arrow.NewSchema([]arrow.Field{
+		{Name: "a", Type: arrow.PrimitiveTypes.Int64},
+	}, nil)
+
+	const data = `[{"a": 9223372036854775807}, {"a": -9223372036854775808}]`
+	//nolint:staticcheck // SA1019: explicitly verifying deprecated WithUseNumber still works
+	batch, _, err := array.RecordFromJSON(mem, schema, strings.NewReader(data), array.WithUseNumber())
+	require.NoError(t, err)
+	require.NotNil(t, batch)
+	defer batch.Release()
+
+	col := batch.Column(0).(*array.Int64)
+	assert.EqualValues(t, int64(9223372036854775807), col.Value(0))
+	assert.EqualValues(t, int64(-9223372036854775808), col.Value(1))
+}
+
+func TestRecordFromJSONLargeDuration(t *testing.T) {
+	mem := memory.NewCheckedAllocator(memory.DefaultAllocator)
+	defer mem.AssertSize(t, 0)
+
+	schema := arrow.NewSchema([]arrow.Field{
+		{Name: "a", Type: arrow.FixedWidthTypes.Duration_s},
+	}, nil)
+
+	const data = `[{"a": 9223372036854775807}]`
+	batch, _, err := array.RecordFromJSON(mem, schema, strings.NewReader(data))
+	require.NoError(t, err)
+	require.NotNil(t, batch)
+	defer batch.Release()
+
+	col := batch.Column(0).(*array.Duration)
+	assert.EqualValues(t, arrow.Duration(9223372036854775807), col.Value(0))
+}
+
+func TestRecordBuilderUnmarshalJSONLargeInt64(t *testing.T) {
+	mem := memory.NewCheckedAllocator(memory.DefaultAllocator)
+	defer mem.AssertSize(t, 0)
+
+	schema := arrow.NewSchema([]arrow.Field{
+		{Name: "a", Type: arrow.PrimitiveTypes.Int64},
+	}, nil)
+
+	bldr := array.NewRecordBuilder(mem, schema)
+	defer bldr.Release()
+
+	require.NoError(t, bldr.UnmarshalJSON([]byte(`{"a": 9223372036854775807}`)))
+
+	rec := bldr.NewRecordBatch()
+	defer rec.Release()
+
+	col := rec.Column(0).(*array.Int64)
+	assert.EqualValues(t, int64(9223372036854775807), col.Value(0))
+}
+
+func TestRecordBuilderUnmarshalOnePreservesUserDecoderOptions(t *testing.T) {
+	mem := memory.NewCheckedAllocator(memory.DefaultAllocator)
+	defer mem.AssertSize(t, 0)
+
+	schema := arrow.NewSchema([]arrow.Field{
+		{Name: "a", Type: arrow.PrimitiveTypes.Int64},
+	}, nil)
+
+	bldr := array.NewRecordBuilder(mem, schema)
+	defer bldr.Release()
+
+	src := strings.NewReader(`{"a": 9223372036854775807}`)
+	dec := json.NewDecoder(src)
+	dec.UseNumber()
+
+	require.NoError(t, bldr.UnmarshalOne(dec))
+
+	rec := bldr.NewRecordBatch()
+	defer rec.Release()
+
+	col := rec.Column(0).(*array.Int64)
+	assert.EqualValues(t, int64(9223372036854775807), col.Value(0))
+}
+
+func TestDurationBuilderJSONStringInteger(t *testing.T) {
+	mem := memory.NewCheckedAllocator(memory.DefaultAllocator)
+	defer mem.AssertSize(t, 0)
+
+	arr, _, err := array.FromJSON(mem, arrow.FixedWidthTypes.Duration_s,
+		strings.NewReader(`["9223372036854775807"]`))
+	require.NoError(t, err)
+	require.NotNil(t, arr)
+	defer arr.Release()
+
+	col := arr.(*array.Duration)
+	assert.EqualValues(t, arrow.Duration(9223372036854775807), col.Value(0))
+}
+
+func TestTimestampBuilderJSONStringInteger(t *testing.T) {
+	mem := memory.NewCheckedAllocator(memory.DefaultAllocator)
+	defer mem.AssertSize(t, 0)
+
+	arr, _, err := array.FromJSON(mem, arrow.FixedWidthTypes.Timestamp_s,
+		strings.NewReader(`["9223372036854775807"]`))
+	require.NoError(t, err)
+	require.NotNil(t, arr)
+	defer arr.Release()
+
+	col := arr.(*array.Timestamp)
+	assert.EqualValues(t, arrow.Timestamp(9223372036854775807), col.Value(0))
+}
+
+func TestTime32BuilderJSONStringInteger(t *testing.T) {
+	mem := memory.NewCheckedAllocator(memory.DefaultAllocator)
+	defer mem.AssertSize(t, 0)
+
+	arr, _, err := array.FromJSON(mem, arrow.FixedWidthTypes.Time32s,
+		strings.NewReader(`["2147483647"]`))
+	require.NoError(t, err)
+	require.NotNil(t, arr)
+	defer arr.Release()
+
+	col := arr.(*array.Time32)
+	assert.EqualValues(t, arrow.Time32(2147483647), col.Value(0))
+}
+
+func TestTime64BuilderJSONStringInteger(t *testing.T) {
+	mem := memory.NewCheckedAllocator(memory.DefaultAllocator)
+	defer mem.AssertSize(t, 0)
+
+	arr, _, err := array.FromJSON(mem, arrow.FixedWidthTypes.Time64us,
+		strings.NewReader(`["9223372036854775807"]`))
+	require.NoError(t, err)
+	require.NotNil(t, arr)
+	defer arr.Release()
+
+	col := arr.(*array.Time64)
+	assert.EqualValues(t, arrow.Time64(9223372036854775807), col.Value(0))
+}
+
+func TestDate32BuilderJSONStringInteger(t *testing.T) {
+	mem := memory.NewCheckedAllocator(memory.DefaultAllocator)
+	defer mem.AssertSize(t, 0)
+
+	arr, _, err := array.FromJSON(mem, arrow.FixedWidthTypes.Date32,
+		strings.NewReader(`["2147483647"]`))
+	require.NoError(t, err)
+	require.NotNil(t, arr)
+	defer arr.Release()
+
+	col := arr.(*array.Date32)
+	assert.EqualValues(t, arrow.Date32(2147483647), col.Value(0))
+}
+
+func TestDate64BuilderJSONStringInteger(t *testing.T) {
+	mem := memory.NewCheckedAllocator(memory.DefaultAllocator)
+	defer mem.AssertSize(t, 0)
+
+	arr, _, err := array.FromJSON(mem, arrow.FixedWidthTypes.Date64,
+		strings.NewReader(`["9223372036854775807"]`))
+	require.NoError(t, err)
+	require.NotNil(t, arr)
+	defer arr.Release()
+
+	col := arr.(*array.Date64)
+	assert.EqualValues(t, arrow.Date64(9223372036854775807), col.Value(0))
+}
+
+func TestDurationBuilderJSONStringIntegerInvalid(t *testing.T) {
+	mem := memory.NewCheckedAllocator(memory.DefaultAllocator)
+	defer mem.AssertSize(t, 0)
+
+	_, _, err := array.FromJSON(mem, arrow.FixedWidthTypes.Duration_s,
+		strings.NewReader(`["abc"]`))
+	assert.Error(t, err)
+}
+
+func TestDurationBuilderJSONStringDurationFormat(t *testing.T) {
+	mem := memory.NewCheckedAllocator(memory.DefaultAllocator)
+	defer mem.AssertSize(t, 0)
+
+	arr, _, err := array.FromJSON(mem, arrow.FixedWidthTypes.Duration_s,
+		strings.NewReader(`["3h2m0.5s"]`))
+	require.NoError(t, err)
+	require.NotNil(t, arr)
+	defer arr.Release()
+
+	col := arr.(*array.Duration)
+	assert.EqualValues(t, arrow.Duration(10920), col.Value(0))
+}
+
+// TestJSONNumberStrictValidation verifies that with UseNumber always enabled
+// (issue #804), invalid integer JSON inputs are rejected rather than silently
+// truncated, wrapped, or coerced.
+func TestJSONNumberStrictValidation(t *testing.T) {
+	cases := []struct {
+		name string
+		dt   arrow.DataType
+		json string
+	}{
+		{"Int64Fractional", arrow.PrimitiveTypes.Int64, `[1.5]`},
+		{"Int8OutOfRangePositive", arrow.PrimitiveTypes.Int8, `[128]`},
+		{"Int8OutOfRangeNegative", arrow.PrimitiveTypes.Int8, `[-129]`},
+		{"Int16OutOfRange", arrow.PrimitiveTypes.Int16, `[32768]`},
+		{"Uint8Negative", arrow.PrimitiveTypes.Uint8, `[-1]`},
+		{"Uint8Fractional", arrow.PrimitiveTypes.Uint8, `[0.5]`},
+		{"Uint16OutOfRange", arrow.PrimitiveTypes.Uint16, `[65536]`},
+		{"Uint64Negative", arrow.PrimitiveTypes.Uint64, `[-1]`},
+		{"Uint64ExactBoundary", arrow.PrimitiveTypes.Uint64, `[18446744073709551616]`},
+		{"Uint64ExponentialOverflow", arrow.PrimitiveTypes.Uint64, `[1.8446744073709552e+19]`},
+		{"DurationFractional", arrow.FixedWidthTypes.Duration_s, `[1.5]`},
+		{"TimestampFractional", arrow.FixedWidthTypes.Timestamp_s, `[1.5]`},
+		{"Date32Fractional", arrow.FixedWidthTypes.Date32, `[1.5]`},
+		{"Date32OverflowPositive", arrow.FixedWidthTypes.Date32, `[2147483648]`},
+		{"Date32OverflowNegative", arrow.FixedWidthTypes.Date32, `[-2147483649]`},
+		{"Date64Fractional", arrow.FixedWidthTypes.Date64, `[1.5]`},
+		{"Time32Fractional", arrow.FixedWidthTypes.Time32s, `[1.5]`},
+		{"Time32OverflowPositive", arrow.FixedWidthTypes.Time32s, `[2147483648]`},
+		{"Time32OverflowNegative", arrow.FixedWidthTypes.Time32s, `[-2147483649]`},
+		{"Time64Fractional", arrow.FixedWidthTypes.Time64us, `[1.5]`},
+		{"Int64NaNString", arrow.PrimitiveTypes.Int64, `["NaN"]`},
+		{"Int64InfString", arrow.PrimitiveTypes.Int64, `["+Inf"]`},
+		{"Uint64NaNString", arrow.PrimitiveTypes.Uint64, `["NaN"]`},
+		{"Uint64InfString", arrow.PrimitiveTypes.Uint64, `["+Inf"]`},
+		{"Uint64ExponentialOverflowString", arrow.PrimitiveTypes.Uint64, `["1.8446744073709552e+19"]`},
+		{"Int64ExponentBeyondMantissa", arrow.PrimitiveTypes.Int64, `[9.007199254740993e15]`},
+		{"Uint64ExponentBeyondMantissa", arrow.PrimitiveTypes.Uint64, `[9.007199254740993e15]`},
+		{"Int64ExponentBeyondMantissaString", arrow.PrimitiveTypes.Int64, `["9.007199254740993e15"]`},
+		{"Uint64ExponentBeyondMantissaString", arrow.PrimitiveTypes.Uint64, `["9.007199254740993e15"]`},
+	}
+	for _, tc := range cases {
+		t.Run(tc.name, func(t *testing.T) {
+			mem := memory.NewCheckedAllocator(memory.DefaultAllocator)
+			defer mem.AssertSize(t, 0)
+
+			arr, _, err := array.FromJSON(mem, tc.dt, strings.NewReader(tc.json))
+			if err == nil {
+				arr.Release()
+				t.Fatalf("expected error for %s with input %s, got nil", tc.name, tc.json)
+			}
+		})
+	}
+}
diff --git a/arrow/compute/arithmetic_test.go b/arrow/compute/arithmetic_test.go
index 07fb1fc9..67e22e8e 100644
--- a/arrow/compute/arithmetic_test.go
+++ b/arrow/compute/arithmetic_test.go
@@ -154,7 +154,7 @@ func (b *BinaryFuncTestSuite) TearDownTest() {
 }
 
 func (b *BinaryFuncTestSuite) getArr(dt arrow.DataType, str string) arrow.Array {
-	arr, _, err := array.FromJSON(b.mem, dt, strings.NewReader(str), array.WithUseNumber())
+	arr, _, err := array.FromJSON(b.mem, dt, strings.NewReader(str))
 	b.Require().NoError(err)
 	return arr
 }
@@ -164,9 +164,9 @@ type Float16BinaryFuncTestSuite struct {
 }
 
 func (b *Float16BinaryFuncTestSuite) assertBinopErr(fn binaryFunc, lhs, rhs string) {
-	left, _, _ := array.FromJSON(b.mem, arrow.FixedWidthTypes.Float16, strings.NewReader(lhs), array.WithUseNumber())
+	left, _, _ := array.FromJSON(b.mem, arrow.FixedWidthTypes.Float16, strings.NewReader(lhs))
 	defer left.Release()
-	right, _, _ := array.FromJSON(b.mem, arrow.FixedWidthTypes.Float16, strings.NewReader(rhs), array.WithUseNumber())
+	right, _, _ := array.FromJSON(b.mem, arrow.FixedWidthTypes.Float16, strings.NewReader(rhs))
 	defer right.Release()
 
 	_, err := fn(&compute.ArrayDatum{left.Data()}, &compute.ArrayDatum{right.Data()})
@@ -288,20 +288,20 @@ func (b *BinaryArithmeticSuite[T]) assertBinopArrs(fn binaryArithmeticFunc, lhs,
 }
 
 func (b *BinaryArithmeticSuite[T]) assertBinopExpArr(fn binaryArithmeticFunc, lhs, rhs string, exp arrow.Array) {
-	left, _, _ := array.FromJSON(b.mem, b.DataType(), strings.NewReader(lhs), array.WithUseNumber())
+	left, _, _ := array.FromJSON(b.mem, b.DataType(), strings.NewReader(lhs))
 	defer left.Release()
-	right, _, _ := array.FromJSON(b.mem, b.DataType(), strings.NewReader(rhs), array.WithUseNumber())
+	right, _, _ := array.FromJSON(b.mem, b.DataType(), strings.NewReader(rhs))
 	defer right.Release()
 
 	b.assertBinopArrs(fn, left, right, exp)
 }
 
 func (b *BinaryArithmeticSuite[T]) assertBinop(fn binaryArithmeticFunc, lhs, rhs, expected string) {
-	left, _, _ := array.FromJSON(b.mem, b.DataType(), strings.NewReader(lhs), array.WithUseNumber())
+	left, _, _ := array.FromJSON(b.mem, b.DataType(), strings.NewReader(lhs))
 	defer left.Release()
-	right, _, _ := array.FromJSON(b.mem, b.DataType(), strings.NewReader(rhs), array.WithUseNumber())
+	right, _, _ := array.FromJSON(b.mem, b.DataType(), strings.NewReader(rhs))
 	defer right.Release()
-	exp, _, _ := array.FromJSON(b.mem, b.DataType(), strings.NewReader(expected), array.WithUseNumber())
+	exp, _, _ := array.FromJSON(b.mem, b.DataType(), strings.NewReader(expected))
 	defer exp.Release()
 
 	b.assertBinopArrs(fn, left, right, exp)
@@ -312,9 +312,9 @@ func (b *BinaryArithmeticSuite[T]) setOverflowCheck(value bool) {
 }
 
 func (b *BinaryArithmeticSuite[T]) assertBinopErr(fn binaryArithmeticFunc, lhs, rhs, expectedMsg string) {
-	left, _, _ := array.FromJSON(b.mem, b.DataType(), strings.NewReader(lhs), array.WithUseNumber())
+	left, _, _ := array.FromJSON(b.mem, b.DataType(), strings.NewReader(lhs))
 	defer left.Release()
-	right, _, _ := array.FromJSON(b.mem, b.DataType(), strings.NewReader(rhs), array.WithUseNumber())
+	right, _, _ := array.FromJSON(b.mem, b.DataType(), strings.NewReader(rhs))
 	defer right.Release()
 
 	assertBinopErr(b.T(), func(left, right compute.Datum) (compute.Datum, error) {
@@ -2449,7 +2449,7 @@ func (us *UnaryArithmeticSuite[T, O]) makeArray(v ...T) arrow.Array {
 }
 
 func (us *UnaryArithmeticSuite[T, O]) getArr(dt arrow.DataType, str string) arrow.Array {
-	arr, _, err := array.FromJSON(us.mem, dt, strings.NewReader(str), array.WithUseNumber())
+	arr, _, err := array.FromJSON(us.mem, dt, strings.NewReader(str))
 	us.Require().NoError(err)
 	return arr
 }
@@ -2504,7 +2504,7 @@ func (us *UnaryArithmeticSuite[T, O]) assertUnaryOpArrs(fn unaryArithmeticFunc[O
 }
 
 func (us *UnaryArithmeticSuite[T, O]) assertUnaryOpExpArr(fn unaryArithmeticFunc[O], arg string, exp arrow.Array) {
-	in, _, err := array.FromJSON(us.mem, us.datatype(), strings.NewReader(arg), array.WithUseNumber())
+	in, _, err := array.FromJSON(us.mem, us.datatype(), strings.NewReader(arg))
 	us.Require().NoError(err)
 	defer in.Release()
 
@@ -2512,10 +2512,10 @@ func (us *UnaryArithmeticSuite[T, O]) assertUnaryOpExpArr(fn unaryArithmeticFunc
 }
 
 func (us *UnaryArithmeticSuite[T, O]) assertUnaryOp(fn unaryArithmeticFunc[O], arg, exp string) {
-	in, _, err := array.FromJSON(us.mem, us.datatype(), strings.NewReader(arg), array.WithUseNumber())
+	in, _, err := array.FromJSON(us.mem, us.datatype(), strings.NewReader(arg))
 	us.Require().NoError(err)
 	defer in.Release()
-	expected, _, err := array.FromJSON(us.mem, us.datatype(), strings.NewReader(exp), array.WithUseNumber())
+	expected, _, err := array.FromJSON(us.mem, us.datatype(), strings.NewReader(exp))
 	us.Require().NoError(err)
 	defer expected.Release()
 
@@ -2523,7 +2523,7 @@ func (us *UnaryArithmeticSuite[T, O]) assertUnaryOp(fn unaryArithmeticFunc[O], a
 }
 
 func (us *UnaryArithmeticSuite[T, O]) assertUnaryOpErr(fn unaryArithmeticFunc[O], arg string, msg string) {
-	in, _, err := array.FromJSON(us.mem, us.datatype(), strings.NewReader(arg), array.WithUseNumber())
+	in, _, err := array.FromJSON(us.mem, us.datatype(), strings.NewReader(arg))
 	us.Require().NoError(err)
 	defer in.Release()
 
diff --git a/arrow/compute/cast_test.go b/arrow/compute/cast_test.go
index a6d2e016..67420098 100644
--- a/arrow/compute/cast_test.go
+++ b/arrow/compute/cast_test.go
@@ -410,15 +410,15 @@ func (c *CastSuite) TestCanCast() {
 }
 
 func (c *CastSuite) checkCastFails(dt arrow.DataType, input string, opts *compute.CastOptions) {
-	inArr, _, _ := array.FromJSON(c.mem, dt, strings.NewReader(input), array.WithUseNumber())
+	inArr, _, _ := array.FromJSON(c.mem, dt, strings.NewReader(input))
 	defer inArr.Release()
 
 	checkCastFails(c.T(), inArr, *opts)
 }
 
 func (c *CastSuite) checkCastOpts(dtIn, dtOut arrow.DataType, inJSON, outJSON string, opts compute.CastOptions) {
-	inArr, _, _ := array.FromJSON(c.mem, dtIn, strings.NewReader(inJSON), array.WithUseNumber())
-	outArr, _, _ := array.FromJSON(c.mem, dtOut, strings.NewReader(outJSON), array.WithUseNumber())
+	inArr, _, _ := array.FromJSON(c.mem, dtIn, strings.NewReader(inJSON))
+	outArr, _, _ := array.FromJSON(c.mem, dtOut, strings.NewReader(outJSON))
 	defer inArr.Release()
 	defer outArr.Release()
 
@@ -430,13 +430,13 @@ func (c *CastSuite) checkCast(dtIn, dtOut arrow.DataType, inJSON, outJSON string
 }
 
 func (c *CastSuite) checkCastArr(in arrow.Array, dtOut arrow.DataType, json string, opts compute.CastOptions) {
-	outArr, _, _ := array.FromJSON(c.mem, dtOut, strings.NewReader(json), array.WithUseNumber())
+	outArr, _, _ := array.FromJSON(c.mem, dtOut, strings.NewReader(json))
 	defer outArr.Release()
 	checkCast(c.T(), in, outArr, opts)
 }
 
 func (c *CastSuite) checkCastExp(dtIn arrow.DataType, inJSON string, exp arrow.Array) {
-	inArr, _, _ := array.FromJSON(c.mem, dtIn, strings.NewReader(inJSON), array.WithUseNumber())
+	inArr, _, _ := array.FromJSON(c.mem, dtIn, strings.NewReader(inJSON))
 	defer inArr.Release()
 	checkCast(c.T(), inArr, exp, *compute.DefaultCastOptions(true))
 }
@@ -525,8 +525,7 @@ func (c *CastSuite) TestIntegerSignedToUnsigned() {
 	checkCast(c.T(), i32s, u32s, options)
 
 	u64s, _, _ := array.FromJSON(c.mem, arrow.PrimitiveTypes.Uint64,
-		strings.NewReader(`[18446744071562067968, null, 18446744073709551615, 65535, 2147483647]`),
-		array.WithUseNumber()) // have to use WithUseNumber so it doesn't lose precision converting to float64
+		strings.NewReader(`[18446744071562067968, null, 18446744073709551615, 65535, 2147483647]`))
 	defer u64s.Release()
 	checkCast(c.T(), i32s, u64s, options)
 
@@ -606,8 +605,7 @@ func (c *CastSuite) TestIntToFloating() {
 	}
 
 	i64s, _, _ := array.FromJSON(c.mem, arrow.PrimitiveTypes.Int64,
-		strings.NewReader(`[-9223372036854775808, -9223372036854775807, 0, 9223372036854775806,  9223372036854775807]`),
-		array.WithUseNumber())
+		strings.NewReader(`[-9223372036854775808, -9223372036854775807, 0, 9223372036854775806,  9223372036854775807]`))
 	defer i64s.Release()
 
 	checkCastFails(c.T(), i64s, *compute.SafeCastOptions(arrow.PrimitiveTypes.Float64))
@@ -666,7 +664,7 @@ func (c *CastSuite) TestDecimal128ToInt() {
 					strings.NewReader(`[
 						"12345678901234567890000.0000000000",
 						"99999999999999999999999.0000000000",
-						null]`), array.WithUseNumber())
+						null]`))
 				defer overflowNoTrunc.Release()
 				opts.AllowIntOverflow = true
 				c.checkCastArr(overflowNoTrunc, arrow.PrimitiveTypes.Int64,
@@ -691,7 +689,7 @@ func (c *CastSuite) TestDecimal128ToInt() {
 							strings.NewReader(`[
 							"12345678901234567890000.0045345000",
 							"99999999999999999999999.0000344300",
-							null]`), array.WithUseNumber())
+							null]`))
 						defer overflowAndTruncate.Release()
 						if opts.AllowIntOverflow && opts.AllowDecimalTruncate {
 							c.checkCastArr(overflowAndTruncate, arrow.PrimitiveTypes.Int64,
@@ -773,7 +771,7 @@ func (c *CastSuite) TestDecimal256ToInt() {
 					strings.NewReader(`[
 						"1234567890123456789000000.0000000000",
 						"9999999999999999999999999.0000000000",
-						null]`), array.WithUseNumber())
+						null]`))
 				defer overflowNoTrunc.Release()
 				opts.AllowIntOverflow = true
 				c.checkCastArr(overflowNoTrunc, arrow.PrimitiveTypes.Int64,
@@ -798,7 +796,7 @@ func (c *CastSuite) TestDecimal256ToInt() {
 							strings.NewReader(`[
 							"1234567890123456789000000.0045345000",
 							"9999999999999999999999999.0000344300",
-							null]`), array.WithUseNumber())
+							null]`))
 						defer overflowAndTruncate.Release()
 						if opts.AllowIntOverflow && opts.AllowDecimalTruncate {
 							c.checkCastArr(overflowAndTruncate, arrow.PrimitiveTypes.Int64,
@@ -1550,7 +1548,7 @@ func (c *CastSuite) TestBinaryLikeToBinaryView() {
 				})
 				defer exp.Release()
 
-				in, _, err := array.FromJSON(c.mem, from, strings.NewReader(strInJSON), array.WithUseNumber())
+				in, _, err := array.FromJSON(c.mem, from, strings.NewReader(strInJSON))
 				c.Require().NoError(err)
 				defer in.Release()
 
@@ -1593,7 +1591,7 @@ func (c *CastSuite) TestBinaryLikeToBinaryView() {
 	c.Run("from fixed_size_binary", func() {
 		fsbType := &arrow.FixedSizeBinaryType{ByteWidth: 3}
 		in, _, err := array.FromJSON(c.mem, fsbType,
-			strings.NewReader(`["YWJj", "ZGVm", null]`), array.WithUseNumber())
+			strings.NewReader(`["YWJj", "ZGVm", null]`))
 		c.Require().NoError(err)
 		defer in.Release()
 
@@ -2433,11 +2431,11 @@ func (c *CastSuite) TestBoolToStringViewStaysSingleBuffer() {
 // scalar iteration path used by checkCast, which relies on scalar.GetScalar
 // - that helper does not yet support view types (unrelated to GH-184).
 func (c *CastSuite) checkCastArrayOnly(dtIn, dtOut arrow.DataType, inJSON, outJSON string) {
-	inArr, _, err := array.FromJSON(c.mem, dtIn, strings.NewReader(inJSON), array.WithUseNumber())
+	inArr, _, err := array.FromJSON(c.mem, dtIn, strings.NewReader(inJSON))
 	c.Require().NoError(err)
 	defer inArr.Release()
 
-	expArr, _, err := array.FromJSON(c.mem, dtOut, strings.NewReader(outJSON), array.WithUseNumber())
+	expArr, _, err := array.FromJSON(c.mem, dtOut, strings.NewReader(outJSON))
 	c.Require().NoError(err)
 	defer expArr.Release()
 
@@ -2672,7 +2670,7 @@ func (c *CastSuite) TestTimestampToDate() {
 		0, 951782400000, -2240524800000, 1999987200000,
 		1577836800000, 1577750400000, 1577664000000, 1262217600000,
 		1262304000000, 1262476800000, 1262563200000, 1136073600000,
-		1135987200000, 1230422400000, 1230508800000, 1325376000000, null]`), array.WithUseNumber())
+		1135987200000, 1230422400000, 1230508800000, 1325376000000, null]`))
 	defer date64.Release()
 
 	checkCast(c.T(), stamps, date32, *compute.DefaultCastOptions(true))
diff --git a/arrow/compute/scalar_compare_test.go b/arrow/compute/scalar_compare_test.go
index 5a3078fc..a615145d 100644
--- a/arrow/compute/scalar_compare_test.go
+++ b/arrow/compute/scalar_compare_test.go
@@ -52,11 +52,11 @@ func (c *CompareSuite) validateCompareDatum(op kernels.CompareOperator, lhs, rhs
 }
 
 func (c *CompareSuite) validateCompare(op kernels.CompareOperator, dt arrow.DataType, lhsStr, rhsStr, expStr string) {
-	lhs, _, err := array.FromJSON(c.mem, dt, strings.NewReader(lhsStr), array.WithUseNumber())
+	lhs, _, err := array.FromJSON(c.mem, dt, strings.NewReader(lhsStr))
 	c.Require().NoError(err)
-	rhs, _, err := array.FromJSON(c.mem, dt, strings.NewReader(rhsStr), array.WithUseNumber())
+	rhs, _, err := array.FromJSON(c.mem, dt, strings.NewReader(rhsStr))
 	c.Require().NoError(err)
-	exp, _, err := array.FromJSON(c.mem, arrow.FixedWidthTypes.Boolean, strings.NewReader(expStr), array.WithUseNumber())
+	exp, _, err := array.FromJSON(c.mem, arrow.FixedWidthTypes.Boolean, strings.NewReader(expStr))
 	c.Require().NoError(err)
 	defer func() {
 		lhs.Release()
@@ -67,9 +67,9 @@ func (c *CompareSuite) validateCompare(op kernels.CompareOperator, dt arrow.Data
 }
 
 func (c *CompareSuite) validateCompareArrScalar(op kernels.CompareOperator, dt arrow.DataType, lhsStr string, rhs compute.Datum, expStr string) {
-	lhs, _, err := array.FromJSON(c.mem, dt, strings.NewReader(lhsStr), array.WithUseNumber())
+	lhs, _, err := array.FromJSON(c.mem, dt, strings.NewReader(lhsStr))
 	c.Require().NoError(err)
-	exp, _, err := array.FromJSON(c.mem, arrow.FixedWidthTypes.Boolean, strings.NewReader(expStr), array.WithUseNumber())
+	exp, _, err := array.FromJSON(c.mem, arrow.FixedWidthTypes.Boolean, strings.NewReader(expStr))
 	c.Require().NoError(err)
 	defer func() {
 		lhs.Release()
@@ -79,9 +79,9 @@ func (c *CompareSuite) validateCompareArrScalar(op kernels.CompareOperator, dt a
 }
 
 func (c *CompareSuite) validateCompareScalarArr(op kernels.CompareOperator, dt arrow.DataType, lhs compute.Datum, rhsStr string, expStr string) {
-	rhs, _, err := array.FromJSON(c.mem, dt, strings.NewReader(rhsStr), array.WithUseNumber())
+	rhs, _, err := array.FromJSON(c.mem, dt, strings.NewReader(rhsStr))
 	c.Require().NoError(err)
-	exp, _, err := array.FromJSON(c.mem, arrow.FixedWidthTypes.Boolean, strings.NewReader(expStr), array.WithUseNumber())
+	exp, _, err := array.FromJSON(c.mem, arrow.FixedWidthTypes.Boolean, strings.NewReader(expStr))
 	c.Require().NoError(err)
 	defer func() {
 		rhs.Release()
@@ -1323,7 +1323,7 @@ func TestCompareGreaterWithImplicitCasts(t *testing.T) {
 	defer mem.AssertSize(t, 0)
 
 	getArr := func(ty arrow.DataType, str string) arrow.Array {
-		arr, _, err := array.FromJSON(mem, ty, strings.NewReader(str), array.WithUseNumber())
+		arr, _, err := array.FromJSON(mem, ty, strings.NewReader(str))
 		require.NoError(t, err)
 		return arr
 	}
@@ -1377,7 +1377,7 @@ func TestCompareGreaterWithImplicitCastUint64EdgeCase(t *testing.T) {
 	defer mem.AssertSize(t, 0)
 
 	getArr := func(ty arrow.DataType, str string) arrow.Array {
-		arr, _, err := array.FromJSON(mem, ty, strings.NewReader(str), array.WithUseNumber())
+		arr, _, err := array.FromJSON(mem, ty, strings.NewReader(str))
 		require.NoError(t, err)
 		return arr
 	}
@@ -1455,7 +1455,7 @@ func (sv *ScalarValiditySuite) TearDownTest() {
 }
 
 func (sv *ScalarValiditySuite) getArr(dt arrow.DataType, str string) arrow.Array {
-	arr, _, err := array.FromJSON(sv.mem, dt, strings.NewReader(str), array.WithUseNumber())
+	arr, _, err := array.FromJSON(sv.mem, dt, strings.NewReader(str))
 	sv.Require().NoError(err)
 	return arr
 }
diff --git a/arrow/compute/scalar_set_lookup_test.go b/arrow/compute/scalar_set_lookup_test.go
index 010bb52c..18b9a78e 100644
--- a/arrow/compute/scalar_set_lookup_test.go
+++ b/arrow/compute/scalar_set_lookup_test.go
@@ -44,7 +44,7 @@ func (ss *ScalarSetLookupSuite) SetupTest() {
 }
 
 func (ss *ScalarSetLookupSuite) getArr(dt arrow.DataType, str string) arrow.Array {
-	arr, _, err := array.FromJSON(ss.mem, dt, strings.NewReader(str), array.WithUseNumber())
+	arr, _, err := array.FromJSON(ss.mem, dt, strings.NewReader(str))
 	ss.Require().NoError(err)
 	return arr
 }
diff --git a/arrow/compute/vector_selection_test.go b/arrow/compute/vector_selection_test.go
index 62fcce08..5a25bfc3 100644
--- a/arrow/compute/vector_selection_test.go
+++ b/arrow/compute/vector_selection_test.go
@@ -61,7 +61,7 @@ func (f *FilterKernelTestSuite) TearDownTest() {
 }
 
 func (f *FilterKernelTestSuite) getArr(dt arrow.DataType, str string) arrow.Array {
-	arr, _, err := array.FromJSON(f.mem, dt, strings.NewReader(str), array.WithUseNumber())
+	arr, _, err := array.FromJSON(f.mem, dt, strings.NewReader(str))
 	f.Require().NoError(err)
 	return arr
 }
@@ -126,11 +126,11 @@ func (f *FilterKernelTestSuite) assertFilter(values, filter, expected arrow.Arra
 }
 
 func (f *FilterKernelTestSuite) assertFilterJSON(dt arrow.DataType, values, filter, expected string) {
-	valuesArr, _, _ := array.FromJSON(f.mem, dt, strings.NewReader(values), array.WithUseNumber())
+	valuesArr, _, _ := array.FromJSON(f.mem, dt, strings.NewReader(values))
 	defer valuesArr.Release()
 	filterArr, _, _ := array.FromJSON(f.mem, arrow.FixedWidthTypes.Boolean, strings.NewReader(filter))
 	defer filterArr.Release()
-	expectedArr, _, _ := array.FromJSON(f.mem, dt, strings.NewReader(expected), array.WithUseNumber())
+	expectedArr, _, _ := array.FromJSON(f.mem, dt, strings.NewReader(expected))
 	defer expectedArr.Release()
 
 	f.assertFilter(valuesArr, filterArr, expectedArr)
@@ -178,7 +178,7 @@ func (tk *TakeKernelTestSuite) assertTakeArrays(values, indices, expected arrow.
 }
 
 func (tk *TakeKernelTestSuite) takeJSON(dt arrow.DataType, values string, idxType arrow.DataType, indices string) (arrow.Array, error) {
-	valArr, _, _ := array.FromJSON(tk.mem, dt, strings.NewReader(values), array.WithUseNumber())
+	valArr, _, _ := array.FromJSON(tk.mem, dt, strings.NewReader(values))
 	defer valArr.Release()
 	indArr, _, _ := array.FromJSON(tk.mem, idxType, strings.NewReader(indices))
 	defer indArr.Release()
@@ -187,9 +187,9 @@ func (tk *TakeKernelTestSuite) takeJSON(dt arrow.DataType, values string, idxTyp
 }
 
 func (tk *TakeKernelTestSuite) checkTake(dt arrow.DataType, valuesJSON, indicesJSON, expJSON string) {
-	values, _, _ := array.FromJSON(tk.mem, dt, strings.NewReader(valuesJSON), array.WithUseNumber())
+	values, _, _ := array.FromJSON(tk.mem, dt, strings.NewReader(valuesJSON))
 	defer values.Release()
-	expected, _, _ := array.FromJSON(tk.mem, dt, strings.NewReader(expJSON), array.WithUseNumber())
+	expected, _, _ := array.FromJSON(tk.mem, dt, strings.NewReader(expJSON))
 	defer expected.Release()
 
 	for _, idxType := range []arrow.DataType{arrow.PrimitiveTypes.Int8, arrow.PrimitiveTypes.Uint32} {
@@ -268,7 +268,7 @@ func (tk *TakeKernelTestSuite) assertNoValidityBitmapButUnknownNullCount(values,
 }
 
 func (tk *TakeKernelTestSuite) assertNoValidityBitmapUnknownNullCountJSON(dt arrow.DataType, values, indices string) {
-	vals, _, _ := array.FromJSON(tk.mem, dt, strings.NewReader(values), array.WithUseNumber())
+	vals, _, _ := array.FromJSON(tk.mem, dt, strings.NewReader(values))
 	defer vals.Release()
 	inds, _, _ := array.FromJSON(tk.mem, arrow.PrimitiveTypes.Int16, strings.NewReader(indices))
 	defer inds.Release()
@@ -821,7 +821,7 @@ type FilterKernelWithRecordBatch struct {
 }
 
 func (f *FilterKernelWithRecordBatch) doFilter(sc *arrow.Schema, batchJSON, selection string, opts compute.FilterOptions) (arrow.RecordBatch, error) {
-	rec, _, err := array.RecordFromJSON(f.mem, sc, strings.NewReader(batchJSON), array.WithUseNumber())
+	rec, _, err := array.RecordFromJSON(f.mem, sc, strings.NewReader(batchJSON))
 	if err != nil {
 		return nil, err
 	}
@@ -848,7 +848,7 @@ func (f *FilterKernelWithRecordBatch) assertFilter(sc *arrow.Schema, batchJSON,
 	f.Require().NoError(err)
 	defer actual.Release()
 
-	expected, _, err := array.RecordFromJSON(f.mem, sc, strings.NewReader(expectedBatch), array.WithUseNumber())
+	expected, _, err := array.RecordFromJSON(f.mem, sc, strings.NewReader(expectedBatch))
 	f.Require().NoError(err)
 	defer expected.Release()
 
@@ -1781,7 +1781,7 @@ func TestMapPreservesValueNullable(t *testing.T) {
 	ctx := compute.WithAllocator(context.TODO(), mem)
 
 	dt := arrow.MapOf(arrow.BinaryTypes.String, arrow.PrimitiveTypes.Int32)
-	values, _, _ := array.FromJSON(mem, dt, strings.NewReader(`[[{"key": "a", "value": 1}]]`), array.WithUseNumber())
+	values, _, _ := array.FromJSON(mem, dt, strings.NewReader(`[[{"key": "a", "value": 1}]]`))
 	defer values.Release()
 	indices, _, _ := array.FromJSON(mem, arrow.PrimitiveTypes.Int8, strings.NewReader(`[0]`))
 	defer indices.Release()
diff --git a/arrow/doc.go b/arrow/doc.go
index 6985543d..389d2fbd 100644
--- a/arrow/doc.go
+++ b/arrow/doc.go
@@ -36,7 +36,7 @@ package arrow
 
 const PkgVersion = "18.6.0"
 
-//go:generate go run _tools/tmpl/main.go -i -data=numeric.tmpldata type_traits_numeric.gen.go.tmpl type_traits_numeric.gen_test.go.tmpl array/numeric.gen.go.tmpl array/numericbuilder.gen.go.tmpl array/bufferbuilder_numeric.gen.go.tmpl
+//go:generate go run _tools/tmpl/main.go -i -data=numeric.tmpldata type_traits_numeric.gen.go.tmpl type_traits_numeric.gen_test.go.tmpl array/numericbuilder.gen.go.tmpl array/numericbuilder.gen_test.go.tmpl array/bufferbuilder_numeric.gen.go.tmpl
 //go:generate go run _tools/tmpl/main.go -i -data=datatype_numeric.gen.go.tmpldata datatype_numeric.gen.go.tmpl tensor/numeric.gen.go.tmpl tensor/numeric.gen_test.go.tmpl
 //go:generate go run _tools/tmpl/main.go -i -data=scalar/numeric.gen.go.tmpldata scalar/numeric.gen.go.tmpl scalar/numeric.gen_test.go.tmpl
 //go:generate go run ./gen-flatbuffers.go
diff --git a/arrow/flight/flightsql/example/type_info.go b/arrow/flight/flightsql/example/type_info.go
index d9a12f0f..60bbb069 100644
--- a/arrow/flight/flightsql/example/type_info.go
+++ b/arrow/flight/flightsql/example/type_info.go
@@ -62,7 +62,7 @@ func GetTypeInfoResult(mem memory.Allocator) arrow.RecordBatch {
 
 	// reference for creating a boolean() array with only zeros
 	zeroBoolArray, _, err := array.FromJSON(mem, arrow.FixedWidthTypes.Boolean,
-		strings.NewReader(`[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]`), array.WithUseNumber())
+		strings.NewReader(`[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]`))
 	if err != nil {
 		panic(err)
 	}