This is an automated email from the ASF dual-hosted git repository.

pitrou pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/arrow.git


The following commit(s) were added to refs/heads/main by this push:
     new 3834d24666 GH-50627: [C++] Migrate from_string.cc to simdjson (#50653)
3834d24666 is described below

commit 3834d24666f48dcb984bacecf72ee7aaddb69999
Author: Alexander Taepper <[email protected]>
AuthorDate: Tue Jul 28 19:00:10 2026 +0200

    GH-50627: [C++] Migrate from_string.cc to simdjson (#50653)
    
    ### Rationale for this change
    
    This is part of the work for #35460. Back in January I started working on 
this change-set, but after discussions came up regarding the use of the 
`simdjson::dom` vs `simdjson::ondemand` front-end the development went stale. I 
saw that @ Reranko05 started the work on #35460 and already made great 
progress, well done!
    
    I still wanted to put up this change-set as a possible view-point, but feel 
free to disregard @ Reranko05 if you already started your own work.
    
    ### What changes are included in this PR?
    
    This changes `from_string.cc` to use simdjson instead of RapidJSON.
    
    ### Are there any user-facing changes?
    
    Yes, the API has changed to no longer allow non-utf8 compatible strings. 
`simdjson` does not support non-compliant inputs and does not intent to support 
it in the future 
[[1]](https://github.com/simdjson/simdjson/issues/1691#issuecomment-895286180) 
[[2]](https://github.com/simdjson/simdjson/blob/8e6bac94877f2d3d026000d36ce81e0aaf38d26f/doc/basics.md?plain=1#L3701)
 [[3]](https://github.com/simdjson/simdjson/discussions/2125)
    
    All call-sites in tests only used a transformation to non-utf8 json as an 
intermediate result to easily go from `string -> Array`, so I changed these to 
use different helper methods instead.
    
    Another difficult problem arises in the handling of `NaN` and `Inf` 
literals in inputs. `simdjson` follows the json standard, whereas `RapidJSON` 
implemented an extension where the following is a valid input:
    ```
    {"x": Inf, "y": -Inf, "z": NaN, "n": Infinity, "m": -Infinity}
    ```
    
    We need to decide whether to be breaking here or not. This PR now contains 
a non-breaking version of the change. This can be quite elegantly added by 
using the public `ondemand` api's function to retrieve the raw token string in 
the `ConvertNumber` function.
    
    Alternatively, we can wait for the next `simdjson` major release, which 
will ship with support for a `SIMDJSON_ENABLE_NAN_INF` compilation flag as per 
[this pull request](https://github.com/simdjson/simdjson/pull/2696). (this was 
only merged to simdjson main)
    
    ### Are these changes tested?
    
    Yes
    
    * GitHub Issue: #50627
    
    Authored-by: Alexander Taepper <[email protected]>
    Signed-off-by: Antoine Pitrou <[email protected]>
---
 cpp/src/arrow/compute/kernels/scalar_cast_test.cc  |  23 +-
 .../arrow/compute/kernels/scalar_string_test.cc    |  81 ++-
 cpp/src/arrow/extension/json_test.cc               |   4 +-
 cpp/src/arrow/json/from_string.cc                  | 713 +++++++++++++--------
 cpp/src/arrow/json/from_string_test.cc             |  18 +-
 cpp/src/arrow/scalar_test.cc                       |   7 +-
 cpp/src/arrow/testing/builder.h                    |  34 +
 cpp/src/parquet/arrow/arrow_reader_writer_test.cc  |  27 +-
 python/pyarrow/src/arrow/python/gdb.cc             |  46 +-
 9 files changed, 596 insertions(+), 357 deletions(-)

diff --git a/cpp/src/arrow/compute/kernels/scalar_cast_test.cc 
b/cpp/src/arrow/compute/kernels/scalar_cast_test.cc
index 51e6ca534c..364a4bd436 100644
--- a/cpp/src/arrow/compute/kernels/scalar_cast_test.cc
+++ b/cpp/src/arrow/compute/kernels/scalar_cast_test.cc
@@ -31,6 +31,7 @@
 #include "arrow/chunked_array.h"
 #include "arrow/extension_type.h"
 #include "arrow/status.h"
+#include "arrow/testing/builder.h"
 #include "arrow/testing/extension_type.h"
 #include "arrow/testing/gtest_util.h"
 #include "arrow/testing/random.h"
@@ -56,16 +57,7 @@ using internal::checked_pointer_cast;
 namespace compute {
 
 static std::shared_ptr<Array> InvalidUtf8(std::shared_ptr<DataType> type) {
-  return ArrayFromJSON(type,
-                       "["
-                       R"(
-                       "Hi",
-                       "olá mundo",
-                       "你好世界",
-                       "",
-                       )"
-                       "\"\xa0\xa1\""
-                       "]");
+  return BinaryArrayFromStrings(type, {"Hi", "olá mundo", "你好世界", "", 
"\xa0\xa1"});
 }
 
 static std::shared_ptr<Array> FixedSizeInvalidUtf8(std::shared_ptr<DataType> 
type) {
@@ -73,16 +65,7 @@ static std::shared_ptr<Array> 
FixedSizeInvalidUtf8(std::shared_ptr<DataType> typ
     // Assume a particular width for testing
     EXPECT_EQ(3, checked_cast<const FixedSizeBinaryType&>(*type).byte_width());
   }
-  return ArrayFromJSON(type,
-                       "["
-                       R"(
-                       "Hi!",
-                       "lá",
-                       "你",
-                       "   ",
-                       )"
-                       "\"\xa0\xa1\xa2\""
-                       "]");
+  return BinaryArrayFromStrings(type, {"Hi!", "lá", "你", "   ", 
"\xa0\xa1\xa2"});
 }
 
 static std::vector<std::shared_ptr<DataType>> kNumericTypes = {
diff --git a/cpp/src/arrow/compute/kernels/scalar_string_test.cc 
b/cpp/src/arrow/compute/kernels/scalar_string_test.cc
index bf5469ccd8..4969beada6 100644
--- a/cpp/src/arrow/compute/kernels/scalar_string_test.cc
+++ b/cpp/src/arrow/compute/kernels/scalar_string_test.cc
@@ -1217,7 +1217,7 @@ TYPED_TEST(TestStringKernels, Utf8Reverse) {
 
   // inputs with malformed utf8 chars would produce garbage output, but the 
end result
   // would produce arrays with same lengths. Hence checking offset buffer 
equality
-  auto malformed_input = ArrayFromJSON(this->type(), "[\"ɑ\xFFɑa\", 
\"ɽ\xe1\xbdɽa\"]");
+  auto malformed_input = this->MakeArray({"ɑ\xFFɑa", "ɽ\xe1\xbdɽa"});
   const Result<Datum>& res = CallFunction("utf8_reverse", {malformed_input});
   
ASSERT_TRUE(res->array()->buffers[1]->Equals(*malformed_input->data()->buffers[1]));
 }
@@ -1340,7 +1340,7 @@ TYPED_TEST(TestStringKernels, Utf8Upper) {
   this->CheckUnary("utf8_upper", "[\"ɑɑɑɑ\"]", this->type(), "[\"ⱭⱭⱭⱭ\"]");
 
   // Test invalid data
-  auto invalid_input = ArrayFromJSON(this->type(), "[\"ɑa\xFFɑ\", 
\"ɽ\xe1\xbdɽaa\"]");
+  auto invalid_input = this->MakeArray({"ɑa\xFFɑ", "ɽ\xe1\xbdɽaa"});
   EXPECT_RAISES_WITH_MESSAGE_THAT(Invalid, testing::HasSubstr("Invalid UTF8 
sequence"),
                                   CallFunction("utf8_upper", {invalid_input}));
 }
@@ -1362,7 +1362,7 @@ TYPED_TEST(TestStringKernels, Utf8Lower) {
   this->CheckUnary("utf8_lower", "[\"ȺȺȺȺ\"]", this->type(), "[\"ⱥⱥⱥⱥ\"]");
 
   // Test invalid data
-  auto invalid_input = ArrayFromJSON(this->type(), "[\"Ⱥa\xFFⱭ\", 
\"Ɽ\xe1\xbdⱤaA\"]");
+  auto invalid_input = this->MakeArray({"Ⱥa\xFFⱭ", "Ɽ\xe1\xbdⱤaA"});
   EXPECT_RAISES_WITH_MESSAGE_THAT(Invalid, testing::HasSubstr("Invalid UTF8 
sequence"),
                                   CallFunction("utf8_lower", {invalid_input}));
 }
@@ -1382,7 +1382,7 @@ TYPED_TEST(TestStringKernels, Utf8SwapCase) {
                    "[\"HeLLo, wOrLD!\", \"$. a35?\"]");
 
   // Test invalid data
-  auto invalid_input = ArrayFromJSON(this->type(), "[\"Ⱥa\xFFⱭ\", 
\"Ɽ\xe1\xbdⱤaA\"]");
+  auto invalid_input = this->MakeArray({"Ⱥa\xFFⱭ", "Ɽ\xe1\xbdⱤaA"});
   EXPECT_RAISES_WITH_MESSAGE_THAT(Invalid, testing::HasSubstr("Invalid UTF8 
sequence"),
                                   CallFunction("utf8_swapcase", 
{invalid_input}));
 }
@@ -2672,101 +2672,92 @@ TYPED_TEST(TestBinaryKernels, SliceBytesPosPos) {
   SliceOptions options{2, 4};
   this->CheckUnary(
       "binary_slice",
-      "[\"\", \"a\", \"ab\", \"a\xc2\xa2\", \"ab\xc2\xa2\", \"ab\xc2\xffZ\"]",
-      this->type(), "[\"\", \"\", \"\", \"\xa2\", \"\xc2\xa2\", 
\"\xc2\xff\"]", &options);
+      this->MakeArray({"", "a", "ab", "a\xc2\xa2", "ab\xc2\xa2", 
"ab\xc2\xffZ"}),
+      this->MakeArray({"", "", "", "\xa2", "\xc2\xa2", "\xc2\xff"}), &options);
   SliceOptions options_step{1, 5, 2};
   this->CheckUnary(
       "binary_slice",
-      "[\"\", \"a\", \"ab\", \"a\xc2\xa2\", \"ab\xc2\xa2\", \"ab\xc2\xffZ\"]",
-      this->type(), "[\"\", \"\", \"b\", \"\xc2\", \"b\xa2\", \"b\xff\"]", 
&options_step);
+      this->MakeArray({"", "a", "ab", "a\xc2\xa2", "ab\xc2\xa2", 
"ab\xc2\xffZ"}),
+      this->MakeArray({"", "", "b", "\xc2", "b\xa2", "b\xff"}), &options_step);
   SliceOptions options_step_neg{5, 1, -2};
   this->CheckUnary(
       "binary_slice",
-      "[\"\", \"a\", \"ab\", \"a\xc2\xa2\", \"ab\xc2\xa2\", \"ab\xc2\xffZ\"]",
-      this->type(), "[\"\", \"\", \"\", \"\xa2\", \"\xa2\", \"Z\xc2\"]",
-      &options_step_neg);
+      this->MakeArray({"", "a", "ab", "a\xc2\xa2", "ab\xc2\xa2", 
"ab\xc2\xffZ"}),
+      this->MakeArray({"", "", "", "\xa2", "\xa2", "Z\xc2"}), 
&options_step_neg);
   options_step_neg.stop = 0;
   this->CheckUnary(
       "binary_slice",
-      "[\"\", \"a\", \"ab\", \"a\xc2\xa2\", \"aZ\xc2\xa2\", \"ab\xc2\xffZ\"]",
-      this->type(), "[\"\", \"\", \"b\", \"\xa2\", \"\xa2Z\", \"Z\xc2\"]",
-      &options_step_neg);
+      this->MakeArray({"", "a", "ab", "a\xc2\xa2", "aZ\xc2\xa2", 
"ab\xc2\xffZ"}),
+      this->MakeArray({"", "", "b", "\xa2", "\xa2Z", "Z\xc2"}), 
&options_step_neg);
 }
 
 TYPED_TEST(TestBinaryKernels, SliceBytesPosNeg) {
   SliceOptions options{2, -1};
   this->CheckUnary(
       "binary_slice",
-      "[\"\", \"a\", \"ab\", \"a\xc2\xa2\", \"aZ\xc2\xa2\", \"ab\xc2\xffZ\"]",
-      this->type(), "[\"\", \"\", \"\", \"\", \"\xc2\", \"\xc2\xff\"]", 
&options);
+      this->MakeArray({"", "a", "ab", "a\xc2\xa2", "aZ\xc2\xa2", 
"ab\xc2\xffZ"}),
+      this->MakeArray({"", "", "", "", "\xc2", "\xc2\xff"}), &options);
   SliceOptions options_step{1, -1, 2};
   this->CheckUnary(
       "binary_slice",
-      "[\"\", \"a\", \"ab\", \"a\xc2\xa2\", \"aZ\xc2\xa2\", \"ab\xc2\xffZ\"]",
-      this->type(), "[\"\", \"\", \"\", \"\xc2\", \"Z\", \"b\xff\"]", 
&options_step);
+      this->MakeArray({"", "a", "ab", "a\xc2\xa2", "aZ\xc2\xa2", 
"ab\xc2\xffZ"}),
+      this->MakeArray({"", "", "", "\xc2", "Z", "b\xff"}), &options_step);
   SliceOptions options_step_neg{3, -4, -2};
   this->CheckUnary(
       "binary_slice",
-      "[\"\", \"a\", \"ab\", \"Z\xc2\xa2\", \"aZ\xc2\xa2\", \"ab\xc2\xffZ\"]",
-      this->type(), "[\"\", \"a\", \"b\", \"\xa2Z\", \"\xa2Z\", \"\xff\"]",
-      &options_step_neg);
+      this->MakeArray({"", "a", "ab", "Z\xc2\xa2", "aZ\xc2\xa2", 
"ab\xc2\xffZ"}),
+      this->MakeArray({"", "a", "b", "\xa2Z", "\xa2Z", "\xff"}), 
&options_step_neg);
   options_step_neg.stop = -5;
   this->CheckUnary(
       "binary_slice",
-      "[\"\", \"a\", \"ab\", \"Z\xc2\xa2\", \"aZ\xc2\xa2\", \"aP\xc2\xffZ\"]",
-      this->type(), "[\"\", \"a\", \"b\", \"\xa2Z\", \"\xa2Z\", \"\xffP\"]",
-      &options_step_neg);
+      this->MakeArray({"", "a", "ab", "Z\xc2\xa2", "aZ\xc2\xa2", 
"aP\xc2\xffZ"}),
+      this->MakeArray({"", "a", "b", "\xa2Z", "\xa2Z", "\xffP"}), 
&options_step_neg);
 }
 
 TYPED_TEST(TestBinaryKernels, SliceBytesNegNeg) {
   SliceOptions options{-2, -1};
   this->CheckUnary(
       "binary_slice",
-      "[\"\", \"a\", \"ab\", \"Z\xc2\xa2\", \"aZ\xc2\xa2\", \"ab\xc2\xffZ\"]",
-      this->type(), "[\"\", \"\", \"a\", \"\xc2\", \"\xc2\", \"\xff\"]", 
&options);
+      this->MakeArray({"", "a", "ab", "Z\xc2\xa2", "aZ\xc2\xa2", 
"ab\xc2\xffZ"}),
+      this->MakeArray({"", "", "a", "\xc2", "\xc2", "\xff"}), &options);
   SliceOptions options_step{-4, -1, 2};
   this->CheckUnary(
       "binary_slice",
-      "[\"\", \"a\", \"ab\", \"Z\xc2\xa2\", \"aZ\xc2\xa2\", \"aP\xc2\xffZ\"]",
-      this->type(), "[\"\", \"\", \"a\", \"Z\", \"a\xc2\", \"P\xff\"]", 
&options_step);
+      this->MakeArray({"", "a", "ab", "Z\xc2\xa2", "aZ\xc2\xa2", 
"aP\xc2\xffZ"}),
+      this->MakeArray({"", "", "a", "Z", "a\xc2", "P\xff"}), &options_step);
   SliceOptions options_step_neg{-1, -3, -2};
   this->CheckUnary(
       "binary_slice",
-      "[\"\", \"a\", \"ab\", \"Z\xc2\xa2\", \"aZ\xc2\xa2\", \"aP\xc2\xffZ\"]",
-      this->type(), "[\"\", \"a\", \"b\", \"\xa2\", \"\xa2\", \"Z\"]", 
&options_step_neg);
+      this->MakeArray({"", "a", "ab", "Z\xc2\xa2", "aZ\xc2\xa2", 
"aP\xc2\xffZ"}),
+      this->MakeArray({"", "a", "b", "\xa2", "\xa2", "Z"}), &options_step_neg);
   options_step_neg.stop = -4;
   this->CheckUnary(
       "binary_slice",
-      "[\"\", \"a\", \"ab\", \"Z\xc2\xa2\", \"aZ\xc2\xa2\", \"aP\xc2\xffZ\"]",
-      this->type(), "[\"\", \"a\", \"b\", \"\xa2Z\", \"\xa2Z\", \"Z\xc2\"]",
-      &options_step_neg);
+      this->MakeArray({"", "a", "ab", "Z\xc2\xa2", "aZ\xc2\xa2", 
"aP\xc2\xffZ"}),
+      this->MakeArray({"", "a", "b", "\xa2Z", "\xa2Z", "Z\xc2"}), 
&options_step_neg);
 }
 
 TYPED_TEST(TestBinaryKernels, SliceBytesNegPos) {
   SliceOptions options{-2, 4};
   this->CheckUnary(
       "binary_slice",
-      "[\"\", \"a\", \"ab\", \"Z\xc2\xa2\", \"aZ\xc2\xa2\", \"aP\xc2\xffZ\"]",
-      this->type(), "[\"\", \"a\", \"ab\", \"\xc2\xa2\", \"\xc2\xa2\", 
\"\xff\"]",
-      &options);
+      this->MakeArray({"", "a", "ab", "Z\xc2\xa2", "aZ\xc2\xa2", 
"aP\xc2\xffZ"}),
+      this->MakeArray({"", "a", "ab", "\xc2\xa2", "\xc2\xa2", "\xff"}), 
&options);
   SliceOptions options_step{-4, 4, 2};
   this->CheckUnary(
       "binary_slice",
-      "[\"\", \"a\", \"ab\", \"Z\xc2\xa2\", \"aZ\xc2\xa2\", \"aP\xc2\xffZ\"]",
-      this->type(), "[\"\", \"a\", \"a\", \"Z\xa2\", \"a\xc2\", \"P\xff\"]",
-      &options_step);
+      this->MakeArray({"", "a", "ab", "Z\xc2\xa2", "aZ\xc2\xa2", 
"aP\xc2\xffZ"}),
+      this->MakeArray({"", "a", "a", "Z\xa2", "a\xc2", "P\xff"}), 
&options_step);
   SliceOptions options_step_neg{-1, 1, -2};
   this->CheckUnary(
       "binary_slice",
-      "[\"\", \"a\", \"ab\", \"Z\xc2\xa2\", \"aZ\xc2\xa2\", \"aP\xc2\xffZ\"]",
-      this->type(), "[\"\", \"\", \"\", \"\xa2\", \"\xa2\", \"Z\xc2\"]",
-      &options_step_neg);
+      this->MakeArray({"", "a", "ab", "Z\xc2\xa2", "aZ\xc2\xa2", 
"aP\xc2\xffZ"}),
+      this->MakeArray({"", "", "", "\xa2", "\xa2", "Z\xc2"}), 
&options_step_neg);
   options_step_neg.stop = 0;
   this->CheckUnary(
       "binary_slice",
-      "[\"\", \"a\", \"ab\", \"Z\xc2\xa2\", \"aZ\xc2\xa2\", \"aP\xc2\xffZ\"]",
-      this->type(), "[\"\", \"\", \"b\", \"\xa2\", \"\xa2Z\", \"Z\xc2\"]",
-      &options_step_neg);
+      this->MakeArray({"", "a", "ab", "Z\xc2\xa2", "aZ\xc2\xa2", 
"aP\xc2\xffZ"}),
+      this->MakeArray({"", "", "b", "\xa2", "\xa2Z", "Z\xc2"}), 
&options_step_neg);
 }
 
 TYPED_TEST(TestStringKernels, PadAscii) {
diff --git a/cpp/src/arrow/extension/json_test.cc 
b/cpp/src/arrow/extension/json_test.cc
index b938ddb2cf..ae0b34db38 100644
--- a/cpp/src/arrow/extension/json_test.cc
+++ b/cpp/src/arrow/extension/json_test.cc
@@ -20,6 +20,7 @@
 #include "arrow/array/validate.h"
 #include "arrow/ipc/test_common.h"
 #include "arrow/record_batch.h"
+#include "arrow/testing/builder.h"
 #include "arrow/testing/gtest_util.h"
 #include "parquet/exception.h"
 
@@ -64,7 +65,8 @@ TEST_F(TestJsonExtensionType, JsonRoundtrip) {
 TEST_F(TestJsonExtensionType, InvalidUTF8) {
   for (const auto& storage_type : {utf8(), large_utf8(), utf8_view()}) {
     auto json_type = json(storage_type);
-    auto invalid_input = ArrayFromJSON(storage_type, "[\"Ⱥa\xFFⱭ\", 
\"Ɽ\xe1\xbdⱤaA\"]");
+    auto invalid_input =
+        BinaryArrayFromStrings(storage_type, {"Ⱥa\xFFⱭ", "Ɽ\xe1\xbdⱤaA"});
     auto ext_arr = ExtensionType::WrapArray(json_type, invalid_input);
 
     ASSERT_RAISES_WITH_MESSAGE(Invalid,
diff --git a/cpp/src/arrow/json/from_string.cc 
b/cpp/src/arrow/json/from_string.cc
index e35a362f5a..9cb14a92a6 100644
--- a/cpp/src/arrow/json/from_string.cc
+++ b/cpp/src/arrow/json/from_string.cc
@@ -15,10 +15,16 @@
 // specific language governing permissions and limitations
 // under the License.
 
+#include <array>
+#include <cctype>
+#include <cmath>
 #include <cstdint>
+#include <functional>
 #include <sstream>
+#include <string>
 #include <string_view>
 #include <type_traits>
+#include <unordered_map>
 #include <utility>
 #include <vector>
 
@@ -32,23 +38,21 @@
 #include "arrow/array/builder_union.h"
 #include "arrow/chunked_array.h"
 #include "arrow/json/from_string.h"
+#include "arrow/result.h"
 #include "arrow/scalar.h"
+#include "arrow/status.h"
+#include "arrow/type.h"
 #include "arrow/type_traits.h"
 #include "arrow/util/checked_cast.h"
 #include "arrow/util/decimal.h"
 #include "arrow/util/float16.h"
 #include "arrow/util/logging_internal.h"
+#include "arrow/util/unreachable.h"
 #include "arrow/util/value_parsing.h"
 
-#include "arrow/json/rapidjson_defs.h"
+#include <simdjson.h>
 
-#include <rapidjson/document.h>
-#include <rapidjson/error/en.h>
-#include <rapidjson/rapidjson.h>
-#include <rapidjson/reader.h>
-#include <rapidjson/writer.h>
-
-namespace rj = arrow::rapidjson;
+namespace sj = simdjson::ondemand;
 
 namespace arrow {
 
@@ -62,32 +66,104 @@ using ::arrow::internal::checked_pointer_cast;
 
 namespace {
 
-constexpr auto kParseFlags = rj::kParseFullPrecisionFlag | 
rj::kParseNanAndInfFlag;
-
-const char* JsonTypeName(rj::Type json_type) {
-  switch (json_type) {
-    case rapidjson::kNullType:
-      return "null";
-    case rapidjson::kFalseType:
-      return "false";
-    case rapidjson::kTrueType:
-      return "true";
-    case rapidjson::kObjectType:
-      return "object";
-    case rapidjson::kArrayType:
+const char* JsonTypeName(sj::json_type type) {
+  switch (type) {
+    case sj::json_type::array:
       return "array";
-    case rapidjson::kStringType:
-      return "string";
-    case rapidjson::kNumberType:
+    case sj::json_type::object:
+      return "object";
+    case sj::json_type::number:
       return "number";
+    case sj::json_type::string:
+      return "string";
+    case sj::json_type::boolean:
+      return "boolean";
+    case sj::json_type::null:
+      return "null";
     default:
       return "unknown";
   }
 }
 
-Status JSONTypeError(const char* expected_type, rj::Type json_type) {
-  return Status::Invalid("Expected ", expected_type, " or null, got JSON type 
",
-                         JsonTypeName(json_type));
+// Empty struct to represent the type of a simdjson null value
+struct SimdjsonNull {};
+
+template <typename T>
+struct JsonTypeNameOf;
+
+template <>
+struct JsonTypeNameOf<sj::array> {
+  static constexpr const char* kValue = "array";
+};
+template <>
+struct JsonTypeNameOf<sj::object> {
+  static constexpr const char* kValue = "object";
+};
+template <>
+struct JsonTypeNameOf<std::string_view> {
+  static constexpr const char* kValue = "string";
+};
+template <>
+struct JsonTypeNameOf<bool> {
+  static constexpr const char* kValue = "boolean";
+};
+template <>
+struct JsonTypeNameOf<SimdjsonNull> {
+  static constexpr const char* kValue = "null";
+};
+template <>
+struct JsonTypeNameOf<int64_t> {
+  static constexpr const char* kValue = "number";
+};
+template <>
+struct JsonTypeNameOf<uint64_t> {
+  static constexpr const char* kValue = "number";
+};
+template <>
+struct JsonTypeNameOf<double> {
+  static constexpr const char* kValue = "number";
+};
+
+template <typename T>
+constexpr const char* JsonTypeName() {
+  return JsonTypeNameOf<T>::kValue;
+}
+
+template <typename SimdjsonValueType>
+Result<SimdjsonValueType> GetJsonAs(sj::value& value) {
+  SimdjsonValueType typed_value{};
+  simdjson::error_code error_code;
+  if constexpr (std::is_same_v<SimdjsonValueType, SimdjsonNull>) {
+    // simdjson has no get<>() for null; probe it explicitly
+    bool is_null;
+    error_code = value.is_null().get(is_null);
+    if (error_code == simdjson::SUCCESS && !is_null) {
+      error_code = simdjson::INCORRECT_TYPE;
+    }
+  } else {
+    error_code = value.get(typed_value);
+  }
+  if (error_code != simdjson::SUCCESS) {
+    sj::json_type json_type;
+    if (value.type().get(json_type) != simdjson::SUCCESS) {
+      return Status::Invalid("Expected ", JsonTypeName<SimdjsonValueType>(),
+                             " or null, got malformed JSON value");
+    }
+    return Status::Invalid("Expected ", JsonTypeName<SimdjsonValueType>(),
+                           " or null, got JSON type ", 
JsonTypeName(json_type));
+  }
+  return typed_value;
+}
+
+template <typename SimdjsonValueType>
+Result<SimdjsonValueType> GetJsonResult(
+    simdjson::simdjson_result<SimdjsonValueType> element, std::string_view 
error) {
+  SimdjsonValueType typed_value;
+  if (auto error_code = std::move(element).get(typed_value);
+      error_code != simdjson::SUCCESS) {
+    return Status::Invalid(error, simdjson::error_message(error_code));
+  }
+  return typed_value;
 }
 
 class JSONConverter {
@@ -96,11 +172,11 @@ class JSONConverter {
 
   virtual Status Init() { return Status::OK(); }
 
-  virtual Status AppendValue(const rj::Value& json_obj) = 0;
+  virtual Status AppendValue(sj::value& json_obj) = 0;
 
   Status AppendNull() { return this->builder()->AppendNull(); }
 
-  virtual Status AppendValues(const rj::Value& json_array) = 0;
+  virtual Result<int32_t> AppendValues(sj::array& json_array) = 0;
 
   virtual std::shared_ptr<ArrayBuilder> builder() = 0;
 
@@ -124,20 +200,17 @@ Status GetConverter(const std::shared_ptr<DataType>&,
 template <class Derived>
 class ConcreteConverter : public JSONConverter {
  public:
-  Result<int64_t> SizeOfJSONArray(const rj::Value& json_obj) {
-    if (!json_obj.IsArray()) {
-      return JSONTypeError("array", json_obj.GetType());
-    }
-    return json_obj.Size();
-  }
-
-  Status AppendValues(const rj::Value& json_array) final {
+  Result<int32_t> AppendValues(sj::array& json_array) final {
     auto self = static_cast<Derived*>(this);
-    ARROW_ASSIGN_OR_RAISE(auto size, SizeOfJSONArray(json_array));
-    for (uint32_t i = 0; i < size; ++i) {
-      RETURN_NOT_OK(self->AppendValue(json_array[i]));
+    int32_t num_elements = 0;
+    for (auto element : json_array) {
+      ARROW_ASSIGN_OR_RAISE(auto value,
+                            GetJsonResult<sj::value>(
+                                element, "Could not iterate elements of JSON 
array: "));
+      RETURN_NOT_OK(self->AppendValue(value));
+      num_elements++;
     }
-    return Status::OK();
+    return num_elements;
   }
 
   const std::shared_ptr<DataType>& value_type() {
@@ -167,11 +240,9 @@ class NullConverter final : public 
ConcreteConverter<NullConverter> {
     builder_ = std::make_shared<NullBuilder>();
   }
 
-  Status AppendValue(const rj::Value& json_obj) override {
-    if (json_obj.IsNull()) {
-      return AppendNull();
-    }
-    return JSONTypeError("null", json_obj.GetType());
+  Status AppendValue(sj::value& json_obj) override {
+    ARROW_RETURN_NOT_OK(GetJsonAs<SimdjsonNull>(json_obj));
+    return AppendNull();
   }
 
   std::shared_ptr<ArrayBuilder> builder() override { return builder_; }
@@ -190,17 +261,16 @@ class BooleanConverter final : public 
ConcreteConverter<BooleanConverter> {
     builder_ = std::make_shared<BooleanBuilder>();
   }
 
-  Status AppendValue(const rj::Value& json_obj) override {
-    if (json_obj.IsNull()) {
+  Status AppendValue(sj::value& json_obj) override {
+    if (json_obj.is_null()) {
       return AppendNull();
     }
-    if (json_obj.IsBool()) {
-      return builder_->Append(json_obj.GetBool());
+    int64_t int_value;
+    if (json_obj.get(int_value) == simdjson::SUCCESS) {
+      return builder_->Append(int_value != 0);
     }
-    if (json_obj.IsInt()) {
-      return builder_->Append(json_obj.GetInt() != 0);
-    }
-    return JSONTypeError("boolean", json_obj.GetType());
+    ARROW_ASSIGN_OR_RAISE(bool bool_value, GetJsonAs<bool>(json_obj));
+    return builder_->Append(bool_value);
   }
 
   std::shared_ptr<ArrayBuilder> builder() override { return builder_; }
@@ -214,78 +284,119 @@ class BooleanConverter final : public 
ConcreteConverter<BooleanConverter> {
 
 // Convert single signed integer value (also {Date,Time}{32,64} and Timestamp)
 template <typename T>
-enable_if_physical_signed_integer<T, Status> ConvertNumber(const rj::Value& 
json_obj,
+enable_if_physical_signed_integer<T, Status> ConvertNumber(sj::value& json_obj,
                                                            const DataType& 
type,
                                                            typename T::c_type* 
out) {
-  if (json_obj.IsInt64()) {
-    int64_t v64 = json_obj.GetInt64();
-    *out = static_cast<typename T::c_type>(v64);
-    if (*out == v64) {
-      return Status::OK();
-    } else {
-      return Status::Invalid("Value ", v64, " out of bounds for ", type);
-    }
+  *out = static_cast<typename T::c_type>(0);
+  ARROW_ASSIGN_OR_RAISE(int64_t v64, GetJsonAs<int64_t>(json_obj));
+  *out = static_cast<typename T::c_type>(v64);
+  if (*out == v64) {
+    return Status::OK();
   } else {
-    *out = static_cast<typename T::c_type>(0);
-    return JSONTypeError("signed int", json_obj.GetType());
+    return Status::Invalid("Value ", v64, " out of bounds for ", type);
   }
 }
 
 // Convert single unsigned integer value
 template <typename T>
-enable_if_unsigned_integer<T, Status> ConvertNumber(const rj::Value& json_obj,
+enable_if_unsigned_integer<T, Status> ConvertNumber(sj::value& json_obj,
                                                     const DataType& type,
                                                     typename T::c_type* out) {
-  if (json_obj.IsUint64()) {
-    uint64_t v64 = json_obj.GetUint64();
-    *out = static_cast<typename T::c_type>(v64);
-    if (*out == v64) {
-      return Status::OK();
-    } else {
-      return Status::Invalid("Value ", v64, " out of bounds for ", type);
-    }
+  *out = static_cast<typename T::c_type>(0);
+  ARROW_ASSIGN_OR_RAISE(uint64_t v64, GetJsonAs<uint64_t>(json_obj));
+  *out = static_cast<typename T::c_type>(v64);
+  if (*out == v64) {
+    return Status::OK();
   } else {
-    *out = static_cast<typename T::c_type>(0);
-    return JSONTypeError("unsigned int", json_obj.GetType());
+    return Status::Invalid("Value ", v64, " out of bounds for ", type);
+  }
+}
+
+// Match the std::string_view against NaN, Inf, Infinity with optional leading 
minus
+std::optional<double> NonFiniteDoubleFromString(std::string_view str) {
+  if (str == "NaN") {
+    return std::numeric_limits<double>::quiet_NaN();
+  } else if (str == "-NaN") {
+    return -std::numeric_limits<double>::quiet_NaN();
+  } else if (str == "Inf" || str == "Infinity") {
+    return std::numeric_limits<double>::infinity();
+  } else if (str == "-Inf" || str == "-Infinity") {
+    return -std::numeric_limits<double>::infinity();
+  } else {
+    return std::nullopt;
+  }
+}
+
+std::optional<double> NonFiniteDoubleFromRawToken(sj::value& json_obj) {
+  std::string_view token = json_obj.raw_json_token();
+  // The raw token includes any trailing whitespace up to the next token
+  while (!token.empty() && std::isspace(static_cast<unsigned 
char>(token.back()))) {
+    token.remove_suffix(1);
   }
+  return NonFiniteDoubleFromString(token);
 }
 
 // Convert float16/HalfFloatType
 template <typename T>
-enable_if_half_float<T, Status> ConvertNumber(const rj::Value& json_obj,
-                                              const DataType& type, uint16_t* 
out) {
-  if (json_obj.IsDouble()) {
-    double f64 = json_obj.GetDouble();
-    *out = Float16(f64).bits();
-    return Status::OK();
-  } else if (json_obj.IsUint()) {
-    uint32_t u32t = json_obj.GetUint();
-    double f64 = static_cast<double>(u32t);
-    *out = Float16(f64).bits();
-    return Status::OK();
-  } else if (json_obj.IsInt()) {
-    int32_t i32t = json_obj.GetInt();
-    double f64 = static_cast<double>(i32t);
-    *out = Float16(f64).bits();
+enable_if_half_float<T, Status> ConvertNumber(sj::value& json_obj, const 
DataType& type,
+                                              uint16_t* out) {
+  *out = static_cast<uint16_t>(0);
+  if (auto f64 = NonFiniteDoubleFromRawToken(json_obj); f64.has_value()) {
+    *out = Float16(f64.value()).bits();
     return Status::OK();
-  } else {
-    *out = static_cast<uint16_t>(0);
-    return JSONTypeError("unsigned int", json_obj.GetType());
   }
+  ARROW_ASSIGN_OR_RAISE(auto f64, GetJsonAs<double>(json_obj));
+  *out = Float16(f64).bits();
+  return arrow::Status::OK();
 }
 
 // Convert single floating point value
 template <typename T>
-enable_if_physical_floating_point<T, Status> ConvertNumber(const rj::Value& 
json_obj,
+enable_if_physical_floating_point<T, Status> ConvertNumber(sj::value& json_obj,
                                                            const DataType& 
type,
                                                            typename T::c_type* 
out) {
-  if (json_obj.IsNumber()) {
-    *out = static_cast<typename T::c_type>(json_obj.GetDouble());
+  *out = static_cast<typename T::c_type>(0);
+  if (auto f64 = NonFiniteDoubleFromRawToken(json_obj); f64.has_value()) {
+    *out = static_cast<typename T::c_type>(f64.value());
     return Status::OK();
-  } else {
-    *out = static_cast<typename T::c_type>(0);
-    return JSONTypeError("number", json_obj.GetType());
   }
+  ARROW_ASSIGN_OR_RAISE(auto f64, GetJsonAs<double>(json_obj));
+  *out = static_cast<typename T::c_type>(f64);
+  return arrow::Status::OK();
+}
+
+// ------------------------------------------------------------------------
+// Helper to process a JSON array with exactly kExpectedSize elements, calling
+// one handler per element, in order.
+using JsonElementHandler = std::function<Status(sj::value&)>;
+
+template <size_t kExpectedSize>
+Status ProcessJsonArrayElements(
+    sj::array& json_array, const char* error_context,
+    const std::array<JsonElementHandler, kExpectedSize>& handlers) {
+  auto it = json_array.begin();
+  auto end = json_array.end();
+
+  size_t index = 0;
+  for (const auto& handler : handlers) {
+    if (it == end) {
+      return Status::Invalid(error_context, " must have exactly ", 
kExpectedSize,
+                             " elements, had ", index);
+    }
+
+    ARROW_ASSIGN_OR_RAISE(
+        sj::value element,
+        GetJsonResult<sj::value>(*it, "Could not iterate elements of JSON 
array: "));
+    RETURN_NOT_OK(handler(element));
+    ++it;
+    ++index;
+  }
+
+  if (it != end) {
+    return Status::Invalid(error_context, " must have exactly ", kExpectedSize,
+                           " elements, had more");
+  }
+  return Status::OK();
 }
 
 // ------------------------------------------------------------------------
@@ -303,8 +414,8 @@ class IntegerConverter final
 
   Status Init() override { return this->MakeConcreteBuilder(&builder_); }
 
-  Status AppendValue(const rj::Value& json_obj) override {
-    if (json_obj.IsNull()) {
+  Status AppendValue(sj::value& json_obj) override {
+    if (json_obj.is_null()) {
       return this->AppendNull();
     }
     c_type value;
@@ -330,8 +441,8 @@ class FloatConverter final : public 
ConcreteConverter<FloatConverter<Type, Build
 
   Status Init() override { return this->MakeConcreteBuilder(&builder_); }
 
-  Status AppendValue(const rj::Value& json_obj) override {
-    if (json_obj.IsNull()) {
+  Status AppendValue(sj::value& json_obj) override {
+    if (json_obj.is_null()) {
       return this->AppendNull();
     }
     c_type value;
@@ -360,22 +471,19 @@ class DecimalConverter final
 
   Status Init() override { return this->MakeConcreteBuilder(&builder_); }
 
-  Status AppendValue(const rj::Value& json_obj) override {
-    if (json_obj.IsNull()) {
+  Status AppendValue(sj::value& json_obj) override {
+    if (json_obj.is_null()) {
       return this->AppendNull();
     }
-    if (json_obj.IsString()) {
-      int32_t precision, scale;
-      DecimalValue d;
-      auto view = std::string_view(json_obj.GetString(), 
json_obj.GetStringLength());
-      RETURN_NOT_OK(DecimalValue::FromString(view, &d, &precision, &scale));
-      if (scale != decimal_type_->scale()) {
-        return Status::Invalid("Invalid scale for decimal: expected ",
-                               decimal_type_->scale(), ", got ", scale);
-      }
-      return builder_->Append(d);
+    ARROW_ASSIGN_OR_RAISE(auto string_value, 
GetJsonAs<std::string_view>(json_obj));
+    int32_t precision, scale;
+    DecimalValue d;
+    RETURN_NOT_OK(DecimalValue::FromString(string_value, &d, &precision, 
&scale));
+    if (scale != decimal_type_->scale()) {
+      return Status::Invalid("Invalid scale for decimal: expected ",
+                             decimal_type_->scale(), ", got ", scale);
     }
-    return JSONTypeError("decimal string", json_obj.GetType());
+    return builder_->Append(d);
   }
 
   std::shared_ptr<ArrayBuilder> builder() override { return builder_; }
@@ -405,20 +513,18 @@ class TimestampConverter final : public 
ConcreteConverter<TimestampConverter> {
     builder_ = std::make_shared<TimestampBuilder>(type, default_memory_pool());
   }
 
-  Status AppendValue(const rj::Value& json_obj) override {
-    if (json_obj.IsNull()) {
+  Status AppendValue(sj::value& json_obj) override {
+    if (json_obj.is_null()) {
       return this->AppendNull();
     }
     int64_t value;
-    if (json_obj.IsNumber()) {
-      RETURN_NOT_OK(ConvertNumber<Int64Type>(json_obj, *this->type_, &value));
-    } else if (json_obj.IsString()) {
-      std::string_view view(json_obj.GetString(), json_obj.GetStringLength());
+    std::string_view view;
+    if (json_obj.get(view) == simdjson::SUCCESS) {
       if (!ParseValue(*timestamp_type_, view.data(), view.size(), &value)) {
         return Status::Invalid("couldn't parse timestamp from ", view);
       }
     } else {
-      return JSONTypeError("timestamp", json_obj.GetType());
+      RETURN_NOT_OK(ConvertNumber<Int64Type>(json_obj, *this->type_, &value));
     }
     return builder_->Append(value);
   }
@@ -441,21 +547,22 @@ class DayTimeIntervalConverter final
     builder_ = std::make_shared<DayTimeIntervalBuilder>(default_memory_pool());
   }
 
-  Status AppendValue(const rj::Value& json_obj) override {
-    if (json_obj.IsNull()) {
+  Status AppendValue(sj::value& json_obj) override {
+    if (json_obj.is_null()) {
       return this->AppendNull();
     }
+
+    ARROW_ASSIGN_OR_RAISE(auto array, GetJsonAs<sj::array>(json_obj));
+
     DayTimeIntervalType::DayMilliseconds value;
-    if (!json_obj.IsArray()) {
-      return JSONTypeError("array", json_obj.GetType());
-    }
-    if (json_obj.Size() != 2) {
-      return Status::Invalid(
-          "day time interval pair must have exactly two elements, had ", 
json_obj.Size());
-    }
-    RETURN_NOT_OK(ConvertNumber<Int32Type>(json_obj[0], *this->type_, 
&value.days));
-    RETURN_NOT_OK(
-        ConvertNumber<Int32Type>(json_obj[1], *this->type_, 
&value.milliseconds));
+    RETURN_NOT_OK(ProcessJsonArrayElements<2>(
+        array, "day-time interval",
+        {[this, &value](sj::value& elem) {
+           return ConvertNumber<Int32Type>(elem, *this->type_, &value.days);
+         },
+         [this, &value](sj::value& elem) {
+           return ConvertNumber<Int32Type>(elem, *this->type_, 
&value.milliseconds);
+         }}));
     return builder_->Append(value);
   }
 
@@ -473,23 +580,25 @@ class MonthDayNanoIntervalConverter final
     builder_ = 
std::make_shared<MonthDayNanoIntervalBuilder>(default_memory_pool());
   }
 
-  Status AppendValue(const rj::Value& json_obj) override {
-    if (json_obj.IsNull()) {
+  Status AppendValue(sj::value& json_obj) override {
+    if (json_obj.is_null()) {
       return this->AppendNull();
     }
-    MonthDayNanoIntervalType::MonthDayNanos value;
-    if (!json_obj.IsArray()) {
-      return JSONTypeError("array", json_obj.GetType());
-    }
-    if (json_obj.Size() != 3) {
-      return Status::Invalid(
-          "month_day_nano_interval  must have exactly 3 elements, had ", 
json_obj.Size());
-    }
-    RETURN_NOT_OK(ConvertNumber<Int32Type>(json_obj[0], *this->type_, 
&value.months));
-    RETURN_NOT_OK(ConvertNumber<Int32Type>(json_obj[1], *this->type_, 
&value.days));
-    RETURN_NOT_OK(
-        ConvertNumber<Int64Type>(json_obj[2], *this->type_, 
&value.nanoseconds));
 
+    ARROW_ASSIGN_OR_RAISE(auto array, GetJsonAs<sj::array>(json_obj));
+
+    MonthDayNanoIntervalType::MonthDayNanos value;
+    RETURN_NOT_OK(ProcessJsonArrayElements<3>(
+        array, "month-day-nano interval",
+        {[this, &value](sj::value& elem) {
+           return ConvertNumber<Int32Type>(elem, *this->type_, &value.months);
+         },
+         [this, &value](sj::value& elem) {
+           return ConvertNumber<Int32Type>(elem, *this->type_, &value.days);
+         },
+         [this, &value](sj::value& elem) {
+           return ConvertNumber<Int64Type>(elem, *this->type_, 
&value.nanoseconds);
+         }}));
     return builder_->Append(value);
   }
 
@@ -510,16 +619,13 @@ class StringConverter final
 
   Status Init() override { return this->MakeConcreteBuilder(&builder_); }
 
-  Status AppendValue(const rj::Value& json_obj) override {
-    if (json_obj.IsNull()) {
+  Status AppendValue(sj::value& json_obj) override {
+    if (json_obj.is_null()) {
       return this->AppendNull();
     }
-    if (json_obj.IsString()) {
-      auto view = std::string_view(json_obj.GetString(), 
json_obj.GetStringLength());
-      return builder_->Append(view);
-    } else {
-      return JSONTypeError("string", json_obj.GetType());
-    }
+
+    ARROW_ASSIGN_OR_RAISE(auto view, GetJsonAs<std::string_view>(json_obj));
+    return builder_->Append(view);
   }
 
   std::shared_ptr<ArrayBuilder> builder() override { return builder_; }
@@ -541,22 +647,18 @@ class FixedSizeBinaryConverter final
 
   Status Init() override { return this->MakeConcreteBuilder(&builder_); }
 
-  Status AppendValue(const rj::Value& json_obj) override {
-    if (json_obj.IsNull()) {
+  Status AppendValue(sj::value& json_obj) override {
+    if (json_obj.is_null()) {
       return this->AppendNull();
     }
-    if (json_obj.IsString()) {
-      auto view = std::string_view(json_obj.GetString(), 
json_obj.GetStringLength());
-      if (view.length() != static_cast<size_t>(builder_->byte_width())) {
-        std::stringstream ss;
-        ss << "Invalid string length " << view.length() << " in JSON input for 
"
-           << this->type_->ToString();
-        return Status::Invalid(ss.str());
-      }
-      return builder_->Append(view);
-    } else {
-      return JSONTypeError("string", json_obj.GetType());
+    ARROW_ASSIGN_OR_RAISE(auto view, GetJsonAs<std::string_view>(json_obj));
+    if (view.length() != static_cast<size_t>(builder_->byte_width())) {
+      std::stringstream ss;
+      ss << "Invalid string length " << view.length() << " in JSON input for "
+         << this->type_->ToString();
+      return Status::Invalid(ss.str());
     }
+    return builder_->Append(view);
   }
 
   std::shared_ptr<ArrayBuilder> builder() override { return builder_; }
@@ -588,14 +690,18 @@ class VarLengthListLikeConverter final
     return Status::OK();
   }
 
-  Status AppendValue(const rj::Value& json_obj) override {
-    if (json_obj.IsNull()) {
+  Status AppendValue(sj::value& json_obj) override {
+    if (json_obj.is_null()) {
       return this->AppendNull();
     }
+    ARROW_ASSIGN_OR_RAISE(auto array, GetJsonAs<sj::array>(json_obj));
+    size_t num_elements;
+    if (array.count_elements().get(num_elements) != simdjson::SUCCESS) {
+      return Status::Invalid("Malformed JSON array for type ", 
this->type_->ToString());
+    }
     // Extend the child converter with this JSON array
-    ARROW_ASSIGN_OR_RAISE(auto size, this->SizeOfJSONArray(json_obj));
-    RETURN_NOT_OK(builder_->Append(true, size));
-    return child_converter_->AppendValues(json_obj);
+    ARROW_RETURN_NOT_OK(builder_->Append(true, 
static_cast<int64_t>(num_elements)));
+    return child_converter_->AppendValues(array).status();
   }
 
   std::shared_ptr<ArrayBuilder> builder() override { return builder_; }
@@ -623,29 +729,29 @@ class MapConverter final : public 
ConcreteConverter<MapConverter> {
     return Status::OK();
   }
 
-  Status AppendValue(const rj::Value& json_obj) override {
-    if (json_obj.IsNull()) {
+  Status AppendValue(sj::value& json_obj) override {
+    if (json_obj.is_null()) {
       return this->AppendNull();
     }
     RETURN_NOT_OK(builder_->Append());
-    if (!json_obj.IsArray()) {
-      return JSONTypeError("array", json_obj.GetType());
-    }
-    auto size = json_obj.Size();
-    for (uint32_t i = 0; i < size; ++i) {
-      const auto& json_pair = json_obj[i];
-      if (!json_pair.IsArray()) {
-        return JSONTypeError("array", json_pair.GetType());
-      }
-      if (json_pair.Size() != 2) {
-        return Status::Invalid("key item pair must have exactly two elements, 
had ",
-                               json_pair.Size());
-      }
-      if (json_pair[0].IsNull()) {
-        return Status::Invalid("null key is invalid");
-      }
-      RETURN_NOT_OK(key_converter_->AppendValue(json_pair[0]));
-      RETURN_NOT_OK(item_converter_->AppendValue(json_pair[1]));
+    ARROW_ASSIGN_OR_RAISE(auto array, GetJsonAs<sj::array>(json_obj));
+
+    for (auto json_pair_result : array) {
+      ARROW_ASSIGN_OR_RAISE(
+          auto json_pair,
+          GetJsonResult<sj::value>(json_pair_result,
+                                   "Could not iterate elements of JSON array: 
"));
+      ARROW_ASSIGN_OR_RAISE(auto json_pair_array, 
GetJsonAs<sj::array>(json_pair));
+
+      RETURN_NOT_OK(ProcessJsonArrayElements<2>(
+          json_pair_array, "key-item pair",
+          {[this](sj::value& key) {
+             if (key.is_null()) {
+               return Status::Invalid("null key is invalid");
+             }
+             return key_converter_->AppendValue(key);
+           },
+           [this](sj::value& item) { return 
item_converter_->AppendValue(item); }}));
     }
     return Status::OK();
   }
@@ -674,15 +780,16 @@ class FixedSizeListConverter final : public 
ConcreteConverter<FixedSizeListConve
     return Status::OK();
   }
 
-  Status AppendValue(const rj::Value& json_obj) override {
-    if (json_obj.IsNull()) {
+  Status AppendValue(sj::value& json_obj) override {
+    if (json_obj.is_null()) {
       return this->AppendNull();
     }
     RETURN_NOT_OK(builder_->Append());
     // Extend the child converter with this JSON array
-    RETURN_NOT_OK(child_converter_->AppendValues(json_obj));
-    if (json_obj.GetArray().Size() != static_cast<rj::SizeType>(list_size_)) {
-      return Status::Invalid("incorrect list size ", 
json_obj.GetArray().Size());
+    ARROW_ASSIGN_OR_RAISE(auto array, GetJsonAs<sj::array>(json_obj));
+    ARROW_ASSIGN_OR_RAISE(int32_t size, child_converter_->AppendValues(array));
+    if (size != list_size_) {
+      return Status::Invalid("incorrect list size ", size);
     }
     return Status::OK();
   }
@@ -710,6 +817,9 @@ class StructConverter final : public 
ConcreteConverter<StructConverter> {
       child_converters_.push_back(child_converter);
       child_builders.push_back(child_converter->builder());
     }
+    for (int32_t i = 0; i < type_->num_fields(); ++i) {
+      field_index_.emplace(type_->field(i)->name(), i);
+    }
     builder_ = std::make_shared<StructBuilder>(type_, default_memory_pool(),
                                                std::move(child_builders));
     return Status::OK();
@@ -718,45 +828,68 @@ class StructConverter final : public 
ConcreteConverter<StructConverter> {
   // Append a JSON value that is either an array of N elements in order
   // or an object mapping struct names to values (omitted struct members
   // are mapped to null).
-  Status AppendValue(const rj::Value& json_obj) override {
-    if (json_obj.IsNull()) {
+  Status AppendValue(sj::value& json_obj) override {
+    if (json_obj.is_null()) {
       return this->AppendNull();
     }
-    if (json_obj.IsArray()) {
-      auto size = json_obj.Size();
-      auto expected_size = static_cast<uint32_t>(type_->num_fields());
+    sj::array array;
+    if (json_obj.get(array) == simdjson::SUCCESS) {
+      auto expected_size = static_cast<size_t>(type_->num_fields());
+      size_t size;
+      if (array.count_elements().get(size) != simdjson::SUCCESS) {
+        return Status::Invalid("Malformed JSON array for type ", 
type_->ToString());
+      }
       if (size != expected_size) {
         return Status::Invalid("Expected array of size ", expected_size,
                                ", got array of size ", size);
       }
-      for (uint32_t i = 0; i < size; ++i) {
-        RETURN_NOT_OK(child_converters_[i]->AppendValue(json_obj[i]));
+      size_t i = 0;
+      for (auto child : array) {
+        ARROW_ASSIGN_OR_RAISE(auto child_value,
+                              GetJsonResult<sj::value>(
+                                  child, "Could not iterate elements of JSON 
array: "));
+        RETURN_NOT_OK(child_converters_[i]->AppendValue(child_value));
+        ++i;
       }
       return builder_->Append();
     }
-    if (json_obj.IsObject()) {
-      auto remaining = json_obj.MemberCount();
-      auto num_children = type_->num_fields();
-      for (int32_t i = 0; i < num_children; ++i) {
-        const auto& field = type_->field(i);
-        auto it = json_obj.FindMember(field->name());
-        if (it != json_obj.MemberEnd()) {
-          --remaining;
-          RETURN_NOT_OK(child_converters_[i]->AppendValue(it->value));
-        } else {
-          RETURN_NOT_OK(child_converters_[i]->AppendNull());
-        }
+    ARROW_ASSIGN_OR_RAISE(auto object, GetJsonAs<sj::object>(json_obj));
+    // Iterate the object fields in JSON order (the on-demand API is
+    // forward-only, so per-field lookups would be quadratic and would also
+    // compare against raw, still-escaped keys). Fields absent from the JSON
+    // are appended as null afterwards.
+    auto num_fields = type_->num_fields();
+    std::vector<bool> field_seen(num_fields, false);
+    for (auto field_result : object) {
+      ARROW_ASSIGN_OR_RAISE(
+          auto field,
+          GetJsonResult<sj::field>(field_result, "Error getting field of 
object: "));
+      std::string_view key;
+      if (field.unescaped_key(/*allow_replacement=*/false).get(key) !=
+          simdjson::SUCCESS) {
+        return Status::Invalid("Malformed key in JSON object for type ",
+                               type_->ToString());
       }
-      if (remaining > 0) {
-        rj::StringBuffer sb;
-        rj::Writer<rj::StringBuffer> writer(sb);
-        json_obj.Accept(writer);
-        return Status::Invalid("Unexpected members in JSON object for type ",
-                               type_->ToString(), " Object: ", sb.GetString());
+      auto it = field_index_.find(key);
+      if (it == field_index_.end()) {
+        return Status::Invalid("Unexpected member \"", key, "\" in JSON object 
for type ",
+                               type_->ToString());
+      }
+      const int32_t field_num = it->second;
+      if (field_seen[field_num]) {
+        return Status::Invalid("Duplicate member \"", key, "\" in JSON object 
for type ",
+                               type_->ToString());
+      }
+      field_seen[field_num] = true;
+      sj::value value = field.value();
+      RETURN_NOT_OK(child_converters_[field_num]->AppendValue(value));
+    }
+    for (int32_t i = 0; i < num_fields; ++i) {
+      if (!field_seen[i]) {
+        RETURN_NOT_OK(child_converters_[i]->AppendNull());
       }
-      return builder_->Append();
     }
-    return JSONTypeError("array or object", json_obj.GetType());
+    return builder_->Append();
   }
 
   std::shared_ptr<ArrayBuilder> builder() override { return builder_; }
@@ -764,6 +897,8 @@ class StructConverter final : public 
ConcreteConverter<StructConverter> {
  private:
   std::shared_ptr<StructBuilder> builder_;
   std::vector<std::shared_ptr<JSONConverter>> child_converters_;
+  // keys are views into the field names owned by type_
+  std::unordered_map<std::string_view, int32_t> field_index_;
 };
 
 // ------------------------------------------------------------------------
@@ -801,40 +936,43 @@ class UnionConverter final : public 
ConcreteConverter<UnionConverter> {
 
   // Append a JSON value that must be a 2-long array, containing the type_id
   // and value of the UnionArray's slot.
-  Status AppendValue(const rj::Value& json_obj) override {
-    if (json_obj.IsNull()) {
+  Status AppendValue(sj::value& json_obj) override {
+    if (json_obj.is_null()) {
       return this->AppendNull();
     }
-    if (!json_obj.IsArray()) {
-      return JSONTypeError("array", json_obj.GetType());
-    }
-    if (json_obj.Size() != 2) {
-      return Status::Invalid("Expected [type_id, value] pair, got array of 
size ",
-                             json_obj.Size());
-    }
-    const auto& id_obj = json_obj[0];
-    if (!id_obj.IsInt()) {
-      return JSONTypeError("int", id_obj.GetType());
-    }
-
-    auto id = static_cast<int8_t>(id_obj.GetInt());
-    auto child_num = type_id_to_child_num_[id];
-    if (child_num == -1) {
-      return Status::Invalid("type_id ", id, " not found in ", *type_);
-    }
 
-    auto child_converter = child_converters_[child_num];
-    if (mode_ == UnionMode::SPARSE) {
-      RETURN_NOT_OK(checked_cast<SparseUnionBuilder&>(*builder_).Append(id));
-      for (auto&& other_converter : child_converters_) {
-        if (other_converter != child_converter) {
-          RETURN_NOT_OK(other_converter->AppendNull());
-        }
-      }
-    } else {
-      RETURN_NOT_OK(checked_cast<DenseUnionBuilder&>(*builder_).Append(id));
-    }
-    return child_converter->AppendValue(json_obj[1]);
+    ARROW_ASSIGN_OR_RAISE(auto array, GetJsonAs<sj::array>(json_obj));
+
+    int8_t id = 0;
+    std::shared_ptr<JSONConverter> child_converter;
+
+    RETURN_NOT_OK(ProcessJsonArrayElements<2>(
+        array, "[type_id, value] pair",
+        {[this, &id, &child_converter](sj::value& id_elem) {
+           ARROW_ASSIGN_OR_RAISE(auto id_value, GetJsonAs<int64_t>(id_elem));
+           id = static_cast<int8_t>(id_value);
+           auto child_num = type_id_to_child_num_[id];
+           if (child_num == -1) {
+             return Status::Invalid("type_id ", id, " not found in ", *type_);
+           }
+           child_converter = child_converters_[child_num];
+
+           if (mode_ == UnionMode::SPARSE) {
+             
RETURN_NOT_OK(checked_cast<SparseUnionBuilder&>(*builder_).Append(id));
+             for (auto&& other_converter : child_converters_) {
+               if (other_converter != child_converter) {
+                 RETURN_NOT_OK(other_converter->AppendNull());
+               }
+             }
+           } else {
+             
RETURN_NOT_OK(checked_cast<DenseUnionBuilder&>(*builder_).Append(id));
+           }
+           return Status::OK();
+         },
+         [&child_converter](sj::value& value_elem) {
+           return child_converter->AppendValue(value_elem);
+         }}));
+    return Status::OK();
   }
 
   std::shared_ptr<ArrayBuilder> builder() override { return builder_; }
@@ -980,15 +1118,27 @@ Result<std::shared_ptr<Array>> ArrayFromJSONString(const 
std::shared_ptr<DataTyp
   std::shared_ptr<JSONConverter> converter;
   RETURN_NOT_OK(GetConverter(type, &converter));
 
-  rj::Document json_doc;
-  json_doc.Parse<kParseFlags>(json_string.data(), json_string.length());
-  if (json_doc.HasParseError()) {
-    return Status::Invalid("JSON parse error at offset ", 
json_doc.GetErrorOffset(), ": ",
-                           GetParseError_En(json_doc.GetParseError()));
+  simdjson::padded_string padded_string{json_string};
+
+  sj::parser parser;
+  sj::document json_doc;
+  auto error = parser.iterate(padded_string).get(json_doc);
+  if (error) {
+    return Status::Invalid("JSON parse error: ", 
simdjson::error_message(error));
+  }
+  sj::value json_obj;
+  if (auto error_code = json_doc.get_value().get(json_obj);
+      error_code != simdjson::SUCCESS) {
+    return Status::Invalid("JSON parse error: ", 
simdjson::error_message(error_code));
   }
+  ARROW_ASSIGN_OR_RAISE(auto array, GetJsonAs<sj::array>(json_obj));
 
   // The JSON document should be an array, append it
-  RETURN_NOT_OK(converter->AppendValues(json_doc));
+  RETURN_NOT_OK(converter->AppendValues(array));
+  // The parser is lazy and does not look past the end of the array by itself
+  if (!json_doc.at_end()) {
+    return Status::Invalid("JSON parse error: trailing content after JSON 
array");
+  }
   std::shared_ptr<Array> out;
   RETURN_NOT_OK(converter->Finish(&out));
   return out;
@@ -1036,17 +1186,36 @@ Result<std::shared_ptr<Scalar>> ScalarFromJSONString(
   std::shared_ptr<JSONConverter> converter;
   RETURN_NOT_OK(GetConverter(type, &converter));
 
-  rj::Document json_doc;
-  json_doc.Parse<kParseFlags>(json_string.data(), json_string.length());
-  if (json_doc.HasParseError()) {
-    return Status::Invalid("JSON parse error at offset ", 
json_doc.GetErrorOffset(), ": ",
-                           GetParseError_En(json_doc.GetParseError()));
+  simdjson::padded_string padded_string{
+      arrow::internal::JoinToString("[", json_string, "]")};
+
+  sj::parser parser;
+  sj::document json_doc;
+  auto error = parser.iterate(padded_string).get(json_doc);
+  if (error) {
+    return Status::Invalid("JSON parse error: ", 
simdjson::error_message(error));
+  }
+
+  sj::value json_obj;
+  if (auto error_code = json_doc.get_value().get(json_obj);
+      error_code != simdjson::SUCCESS) {
+    return Status::Invalid("JSON parse error: ", 
simdjson::error_message(error_code));
+  }
+  ARROW_ASSIGN_OR_RAISE(auto singleton_array, GetJsonAs<sj::array>(json_obj));
+
+  ARROW_ASSIGN_OR_RAISE(int32_t num_elements, 
converter->AppendValues(singleton_array));
+  if (num_elements != 1) {
+    return Status::Invalid("Expected exactly one JSON value, got ", 
num_elements,
+                           " values");
+  }
+  // The parser is lazy and does not look past the end of the wrapped value by
+  // itself, e.g. the input "1] , [2" would otherwise be accepted as 1
+  if (!json_doc.at_end()) {
+    return Status::Invalid("JSON parse error: trailing content after JSON 
value");
   }
 
   std::shared_ptr<Array> array;
-  RETURN_NOT_OK(converter->AppendValue(json_doc));
   RETURN_NOT_OK(converter->Finish(&array));
-  DCHECK_EQ(array->length(), 1);
   return array->GetScalar(0);
 }
 
diff --git a/cpp/src/arrow/json/from_string_test.cc 
b/cpp/src/arrow/json/from_string_test.cc
index 654450462e..817cfe52ee 100644
--- a/cpp/src/arrow/json/from_string_test.cc
+++ b/cpp/src/arrow/json/from_string_test.cc
@@ -217,6 +217,9 @@ TYPED_TEST_P(TestIntegersFromString, Errors) {
   ASSERT_RAISES(Invalid, ArrayFromJSONString(type, "[0.0]"));
   ASSERT_RAISES(Invalid, ArrayFromJSONString(type, "[\"0\"]"));
   ASSERT_RAISES(Invalid, ArrayFromJSONString(type, "[[0]]"));
+  ASSERT_RAISES(Invalid, ArrayFromJSONString(type, "[0]]"));
+  ASSERT_RAISES(Invalid, ArrayFromJSONString(type, "[0], [1]"));
+  ASSERT_RAISES(Invalid, ArrayFromJSONString(type, "[0] junk"));
 }
 
 TYPED_TEST_P(TestIntegersFromString, OutOfBounds) {
@@ -305,9 +308,9 @@ TYPED_TEST_P(TestStringsFromString, Basics) {
   AssertJSONArray<T, std::string>(type, "[\"\xc3\xa9\"]", {"\xc3\xa9"});
 
   if (!T::is_utf8) {
-    // Arbitrary binary (non-UTF8) sequence in string
+    // Arbitrary binary (non-UTF8) sequences cannot be represented
     s = "\xff\x9f";
-    AssertJSONArray<T, std::string>(type, "[\"" + s + "\"]", {s});
+    ASSERT_RAISES(Invalid, ArrayFromJSONString(type, "[\"" + s + "\"]"));
   }
 
   // Bytes < 0x20 can be represented as JSON unicode escapes
@@ -441,7 +444,8 @@ TEST(TestDoubleFromString, Basics) {
   AssertJSONArray<DoubleType>(type, "[1, 2.5, -3e4]", {1.0, 2.5, -3.0e4});
   AssertJSONArray<DoubleType>(type, "[-0.0, Inf, -Inf, null]", {true, true, 
true, false},
                               {-0.0, INFINITY, -INFINITY, 0.0});
-
+  AssertJSONArray<DoubleType>(type, "[Infinity, -Infinity, null]", {true, 
true, false},
+                              {INFINITY, -INFINITY, 0.0});
   ASSERT_OK_AND_ASSIGN(actual, ArrayFromJSONString(type, "[NaN]"));
   ASSERT_OK(actual->ValidateFull());
   double value = checked_cast<DoubleArray&>(*actual).Value(0);
@@ -536,9 +540,10 @@ TEST(TestFixedSizeBinaryFromString, Basics) {
                                                     {"foo", "bar"});
   AssertJSONArray<FixedSizeBinaryType, std::string>(type, "[null, \"foo\"]",
                                                     {false, true}, {"", 
"foo"});
-  // Arbitrary binary (non-UTF8) sequence in string
+  // Arbitrary binary (non-UTF8) sequences cannot be represented: the JSON
+  // parser requires its input to be valid UTF-8
   std::string s = "\xff\x9f\xcc";
-  AssertJSONArray<FixedSizeBinaryType, std::string>(type, "[\"" + s + "\"]", 
{s});
+  ASSERT_RAISES(Invalid, ArrayFromJSONString(type, "[\"" + s + "\"]"));
 }
 
 TEST(TestFixedSizeBinaryFromString, Errors) {
@@ -1563,6 +1568,9 @@ TEST(TestScalarFromJSONString, Errors) {
   ASSERT_RAISES(Invalid, ScalarFromJSONString(binary(), "[]"));
   ASSERT_RAISES(Invalid, ScalarFromJSONString(boolean(), "0.0"));
   ASSERT_RAISES(Invalid, ScalarFromJSONString(boolean(), "\"true\""));
+  ASSERT_RAISES(Invalid, ScalarFromJSONString(int64(), "1, 2"));
+  ASSERT_RAISES(Invalid, ScalarFromJSONString(int64(), "1] , [2"));
+  ASSERT_RAISES(Invalid, ScalarFromJSONString(int64(), "1 junk"));
 }
 
 TEST(TestDictScalarFromJSONString, Basics) {
diff --git a/cpp/src/arrow/scalar_test.cc b/cpp/src/arrow/scalar_test.cc
index 4096b8cd17..c9e49ab66a 100644
--- a/cpp/src/arrow/scalar_test.cc
+++ b/cpp/src/arrow/scalar_test.cc
@@ -36,6 +36,7 @@
 #include "arrow/memory_pool.h"
 #include "arrow/scalar.h"
 #include "arrow/status.h"
+#include "arrow/testing/builder.h"
 #include "arrow/testing/extension_type.h"
 #include "arrow/testing/gtest_util.h"
 #include "arrow/testing/random.h"
@@ -1395,8 +1396,10 @@ class TestListLikeScalar : public ::testing::Test {
     }
 
     {
-      // Invalid UTF8 in child data
-      ScalarType scalar(ArrayFromJSON(utf8(), "[null, null, \"\xff\"]"));
+      std::shared_ptr<Array> invalid_utf8;
+      ArrayFromVector<StringType, std::string>({false, false, true}, {"", "", 
"\xff"},
+                                               &invalid_utf8);
+      ScalarType scalar(invalid_utf8);
       ASSERT_OK(scalar.Validate());
       ASSERT_RAISES(Invalid, scalar.ValidateFull());
     }
diff --git a/cpp/src/arrow/testing/builder.h b/cpp/src/arrow/testing/builder.h
index 6beb7760e3..2d3cbbe9a4 100644
--- a/cpp/src/arrow/testing/builder.h
+++ b/cpp/src/arrow/testing/builder.h
@@ -92,6 +92,40 @@ void ArrayFromVector(const std::vector<C_TYPE>& values, 
std::shared_ptr<Array>*
   ArrayFromVector<TYPE, C_TYPE>(type, values, out);
 }
 
+// BinaryArrayFromStrings: construct an Array of any binary-like type from
+// string values, dispatching on the runtime type id
+inline std::shared_ptr<Array> BinaryArrayFromStrings(
+    const std::shared_ptr<DataType>& type, const std::vector<std::string>& 
values) {
+  std::shared_ptr<Array> array;
+  switch (type->id()) {
+    case Type::BINARY:
+      ArrayFromVector<BinaryType, std::string>(type, values, &array);
+      break;
+    case Type::STRING:
+      ArrayFromVector<StringType, std::string>(type, values, &array);
+      break;
+    case Type::LARGE_BINARY:
+      ArrayFromVector<LargeBinaryType, std::string>(type, values, &array);
+      break;
+    case Type::LARGE_STRING:
+      ArrayFromVector<LargeStringType, std::string>(type, values, &array);
+      break;
+    case Type::BINARY_VIEW:
+      ArrayFromVector<BinaryViewType, std::string>(type, values, &array);
+      break;
+    case Type::STRING_VIEW:
+      ArrayFromVector<StringViewType, std::string>(type, values, &array);
+      break;
+    case Type::FIXED_SIZE_BINARY:
+      ArrayFromVector<FixedSizeBinaryType, std::string>(type, values, &array);
+      break;
+    default:
+      ADD_FAILURE() << "unsupported type for binary test data: " << 
type->ToString();
+      break;
+  }
+  return array;
+}
+
 // ChunkedArrayFromVector: construct a ChunkedArray from vectors of C values
 
 template <typename TYPE, typename C_TYPE = typename TYPE::c_type>
diff --git a/cpp/src/parquet/arrow/arrow_reader_writer_test.cc 
b/cpp/src/parquet/arrow/arrow_reader_writer_test.cc
index 3bee0e7928..3fcc4ac49c 100644
--- a/cpp/src/parquet/arrow/arrow_reader_writer_test.cc
+++ b/cpp/src/parquet/arrow/arrow_reader_writer_test.cc
@@ -1495,7 +1495,12 @@ class TestBinaryLikeParquetIO : public ParquetIOTestBase 
{
                       const std::shared_ptr<DataType>& fallback_type) {
     const auto specific_array = ::arrow::ArrayFromJSON(specific_type, json);
     const auto fallback_array = ::arrow::ArrayFromJSON(fallback_type, json);
+    CheckRoundTrip(specific_array, fallback_array, binary_type);
+  }
 
+  void CheckRoundTrip(const std::shared_ptr<Array>& specific_array,
+                      const std::shared_ptr<Array>& fallback_array,
+                      ::arrow::Type::type binary_type) {
     // When the original Arrow schema isn't stored, the array is decoded as
     // the fallback type (since there is no specific Parquet logical
     // type for it).
@@ -1521,13 +1526,27 @@ class TestBinaryLikeParquetIO : public 
ParquetIOTestBase {
 };
 
 TEST_F(TestBinaryLikeParquetIO, LargeBinary) {
-  CheckRoundTrip("[\"foo\", \"\", null, \"\xff\"]", 
::arrow::Type::LARGE_BINARY,
-                 ::arrow::large_binary(), ::arrow::binary());
+  const std::vector<bool> is_valid = {true, true, false, true};
+  const std::vector<std::string> values = {"foo", "", "", "\xff"};
+  std::shared_ptr<Array> specific_array;
+  ::arrow::ArrayFromVector<::arrow::LargeBinaryType, std::string>(is_valid, 
values,
+                                                                  
&specific_array);
+  std::shared_ptr<Array> fallback_array;
+  ::arrow::ArrayFromVector<::arrow::BinaryType, std::string>(is_valid, values,
+                                                             &fallback_array);
+  CheckRoundTrip(specific_array, fallback_array, ::arrow::Type::LARGE_BINARY);
 }
 
 TEST_F(TestBinaryLikeParquetIO, BinaryView) {
-  CheckRoundTrip("[\"foo\", \"\", null, \"\xff\"]", ::arrow::Type::BINARY_VIEW,
-                 ::arrow::binary_view(), ::arrow::binary());
+  const std::vector<bool> is_valid = {true, true, false, true};
+  const std::vector<std::string> values = {"foo", "", "", "\xff"};
+  std::shared_ptr<Array> specific_array;
+  ::arrow::ArrayFromVector<::arrow::BinaryViewType, std::string>(is_valid, 
values,
+                                                                 
&specific_array);
+  std::shared_ptr<Array> fallback_array;
+  ::arrow::ArrayFromVector<::arrow::BinaryType, std::string>(is_valid, values,
+                                                             &fallback_array);
+  CheckRoundTrip(specific_array, fallback_array, ::arrow::Type::BINARY_VIEW);
 }
 
 TEST_F(TestBinaryLikeParquetIO, LargeString) {
diff --git a/python/pyarrow/src/arrow/python/gdb.cc 
b/python/pyarrow/src/arrow/python/gdb.cc
index 2a7d2eda4b..89135239f2 100644
--- a/python/pyarrow/src/arrow/python/gdb.cc
+++ b/python/pyarrow/src/arrow/python/gdb.cc
@@ -17,9 +17,13 @@
 
 #include <cstdlib>
 #include <memory>
+#include <optional>
+#include <string>
 #include <utility>
+#include <vector>
 
 #include "arrow/array.h"
+#include "arrow/array/builder_binary.h"
 #include "arrow/chunked_array.h"
 #include "arrow/datum.h"
 #include "arrow/extension/uuid.h"
@@ -29,6 +33,7 @@
 #include "arrow/scalar.h"
 #include "arrow/table.h"
 #include "arrow/type.h"
+#include "arrow/util/checked_cast.h"
 #include "arrow/util/debug.h"
 #include "arrow/util/decimal.h"
 #include "arrow/util/key_value_metadata.h"
@@ -43,6 +48,8 @@ using json::ArrayFromJSONString;
 using json::ChunkedArrayFromJSONString;
 using json::ScalarFromJSONString;
 
+using ::arrow::internal::checked_cast;
+
 namespace gdb {
 
 // Add a nested `arrow` namespace to exercise type lookup from GDB 
(ARROW-15652)
@@ -69,6 +76,25 @@ std::shared_ptr<Array> SliceArrayFromJSON(const 
std::shared_ptr<DataType>& ty,
   }
 }
 
+template <typename BuilderType>
+std::shared_ptr<Array> BinaryArrayFromStrings(
+    const std::shared_ptr<DataType>& type,
+    const std::vector<std::optional<std::string>>& values) {
+  std::unique_ptr<ArrayBuilder> builder;
+  ARROW_CHECK_OK(MakeBuilder(default_memory_pool(), type, &builder));
+  auto& concrete_builder = checked_cast<BuilderType&>(*builder);
+  for (const auto& value : values) {
+    if (value.has_value()) {
+      ARROW_CHECK_OK(concrete_builder.Append(*value));
+    } else {
+      ARROW_CHECK_OK(concrete_builder.AppendNull());
+    }
+  }
+  std::shared_ptr<Array> array;
+  ARROW_CHECK_OK(concrete_builder.Finish(&array));
+  return array;
+}
+
 }  // namespace
 
 void TestSession() {
@@ -448,18 +474,22 @@ void TestSession() {
       decimal256(50, 6), R"([null, 
"-123456789012345678901234567890123456789.012345"])");
   auto heap_decimal128_array_sliced = heap_decimal128_array->Slice(1, 1);
 
-  auto heap_fixed_size_binary_array =
-      SliceArrayFromJSON(fixed_size_binary(3), "[null, \"abc\", 
\"\\u0000\\u001f\xff\"]");
+  auto heap_fixed_size_binary_array = 
BinaryArrayFromStrings<FixedSizeBinaryBuilder>(
+      fixed_size_binary(3), {std::nullopt, "abc", std::string("\x00\x1f\xff", 
3)});
   auto heap_fixed_size_binary_array_zero_width =
       SliceArrayFromJSON(fixed_size_binary(0), R"([null, ""])");
   auto heap_fixed_size_binary_array_sliced = 
heap_fixed_size_binary_array->Slice(1, 1);
 
-  const char* json_binary_array = "[null, \"abcd\", \"\\u0000\\u001f\xff\"]";
-  auto heap_binary_array = SliceArrayFromJSON(binary(), json_binary_array);
-  auto heap_large_binary_array = SliceArrayFromJSON(large_binary(), 
json_binary_array);
-  const char* json_string_array = "[null, \"héhé\", \"invalid \xff char\"]";
-  auto heap_string_array = SliceArrayFromJSON(utf8(), json_string_array);
-  auto heap_large_string_array = SliceArrayFromJSON(large_utf8(), 
json_string_array);
+  const std::vector<std::optional<std::string>> binary_values = {
+      std::nullopt, "abcd", std::string("\x00\x1f\xff", 3)};
+  auto heap_binary_array = BinaryArrayFromStrings<BinaryBuilder>(binary(), 
binary_values);
+  auto heap_large_binary_array =
+      BinaryArrayFromStrings<LargeBinaryBuilder>(large_binary(), 
binary_values);
+  const std::vector<std::optional<std::string>> string_values = {std::nullopt, 
"héhé",
+                                                                 "invalid \xff 
char"};
+  auto heap_string_array = BinaryArrayFromStrings<StringBuilder>(utf8(), 
string_values);
+  auto heap_large_string_array =
+      BinaryArrayFromStrings<LargeStringBuilder>(large_utf8(), string_values);
   auto heap_binary_array_sliced = heap_binary_array->Slice(1, 1);
 
   // ChunkedArray

Reply via email to