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 577d3b9c4bd GH-50901: [C++] Replace RapidJSON with simdjson in tensor
extension types (#50874)
577d3b9c4bd is described below
commit 577d3b9c4bd64ad1bdd12dcaaf115b88ac626e0e
Author: Aaditya Srinivasan <[email protected]>
AuthorDate: Tue Aug 25 19:19:56 2026 +0530
GH-50901: [C++] Replace RapidJSON with simdjson in tensor extension types
(#50874)
### Rationale for this change
This PR continues the simdjson migration by replacing RapidJSON usage in
the `FixedShapeTensorType` and `VariableShapeTensorType` extension types with
simdjson's DOM API.
### Changes
- Replace RapidJSON parsing in `FixedShapeTensorType::Deserialize` with
simdjson.
- Replace RapidJSON parsing in `VariableShapeTensorType::Deserialize` with
simdjson.
- Centralize the reusable simdjson DOM helpers in
`arrow/util/simdjson_internal.h`.
- Add helpers for parsing JSON objects, retrieving optional fields, and
validating JSON arrays and their element types.
- Remove the obsolete RapidJSON-specific helpers and includes from the
tensor extension utilities.
- Preserve existing deserialization behavior and error validation,
including the ordering of metadata-level validation errors.
- Update deserialization tests to match simdjson JSON type names.
* GitHub Issue: #50901
Lead-authored-by: Aaditya Srinivasan <[email protected]>
Co-authored-by: Aaditya Srinivasan
<[email protected]>
Co-authored-by: Antoine Pitrou <[email protected]>
Signed-off-by: Antoine Pitrou <[email protected]>
---
cpp/src/arrow/extension/fixed_shape_tensor.cc | 73 +++++--------
.../arrow/extension/tensor_extension_array_test.cc | 56 ++++++----
cpp/src/arrow/extension/tensor_internal.cc | 14 ---
cpp/src/arrow/extension/tensor_internal.h | 7 --
cpp/src/arrow/extension/variable_shape_tensor.cc | 84 ++++++---------
cpp/src/arrow/util/simdjson_internal.h | 119 +++++++++++++++++++++
6 files changed, 215 insertions(+), 138 deletions(-)
diff --git a/cpp/src/arrow/extension/fixed_shape_tensor.cc
b/cpp/src/arrow/extension/fixed_shape_tensor.cc
index cd3d783479d..6a86d6a7a66 100644
--- a/cpp/src/arrow/extension/fixed_shape_tensor.cc
+++ b/cpp/src/arrow/extension/fixed_shape_tensor.cc
@@ -19,6 +19,8 @@
#include <numeric>
#include <sstream>
+#include <simdjson.h>
+
#include "arrow/extension/fixed_shape_tensor.h"
#include "arrow/extension/tensor_internal.h"
#include "arrow/scalar.h"
@@ -26,16 +28,13 @@
#include "arrow/array/array_nested.h"
#include "arrow/array/array_primitive.h"
#include "arrow/json/json_writer_internal.h"
-#include "arrow/json/rapidjson_defs.h" // IWYU pragma: keep
#include "arrow/tensor.h"
#include "arrow/util/logging_internal.h"
#include "arrow/util/print_internal.h"
+#include "arrow/util/simdjson_internal.h"
#include "arrow/util/sort_internal.h"
#include "arrow/util/string.h"
-#include <rapidjson/document.h>
-
-namespace rj = arrow::rapidjson;
using ::arrow::json::JsonWriter;
namespace arrow::extension {
@@ -116,57 +115,39 @@ Result<std::shared_ptr<DataType>>
FixedShapeTensorType::Deserialize(
return Status::Invalid("Expected FixedSizeList storage type, got ",
storage_type->ToString());
}
+
auto fsl_type =
internal::checked_pointer_cast<FixedSizeListType>(storage_type);
auto value_type = fsl_type->value_type();
- rj::Document document;
- if (document.Parse(serialized_data.data(),
serialized_data.length()).HasParseError() ||
- !document.IsObject() || !document.HasMember("shape") ||
- !document["shape"].IsArray()) {
- return Status::Invalid("Invalid serialized JSON data: ", serialized_data);
- }
- std::vector<int64_t> shape;
- for (const auto& x : document["shape"].GetArray()) {
- if (!x.IsInt64()) {
- return Status::Invalid("shape must contain integers, got ",
- internal::JsonTypeName(x));
- }
- shape.emplace_back(x.GetInt64());
- }
+ simdjson::dom::parser parser;
+ ARROW_ASSIGN_OR_RAISE(auto object, internal::ParseJsonObject(parser,
serialized_data));
+
+ ARROW_ASSIGN_OR_RAISE(auto shape_value,
+ internal::ResolveSimdjsonResult(object.at_key("shape"),
+ "Invalid serialized
JSON data"));
+ ARROW_ASSIGN_OR_RAISE(auto shape, internal::GetJsonIntArray(shape_value,
"shape"));
+ ARROW_ASSIGN_OR_RAISE(auto permutation_value,
+ internal::GetOptionalJsonField(object, "permutation"));
std::vector<int64_t> permutation;
- if (document.HasMember("permutation")) {
- const auto& json_permutation = document["permutation"];
- if (!json_permutation.IsArray()) {
- return Status::Invalid("permutation must be an array, got ",
- internal::JsonTypeName(json_permutation));
- }
- for (const auto& x : json_permutation.GetArray()) {
- if (!x.IsInt64()) {
- return Status::Invalid("permutation must contain integers, got ",
- internal::JsonTypeName(x));
- }
- permutation.emplace_back(x.GetInt64());
- }
+ if (permutation_value.has_value()) {
+ ARROW_ASSIGN_OR_RAISE(permutation,
+ internal::GetJsonIntArray(*permutation_value,
"permutation"));
+
if (shape.size() != permutation.size()) {
return Status::Invalid("Invalid permutation");
}
RETURN_NOT_OK(internal::IsPermutationValid(permutation));
}
+
+ ARROW_ASSIGN_OR_RAISE(auto dim_names_value,
+ internal::GetOptionalJsonField(object, "dim_names"));
+
std::vector<std::string> dim_names;
- if (document.HasMember("dim_names")) {
- const auto& json_dim_names = document["dim_names"];
- if (!json_dim_names.IsArray()) {
- return Status::Invalid("dim_names must be an array, got ",
- internal::JsonTypeName(json_dim_names));
- }
- for (const auto& x : json_dim_names.GetArray()) {
- if (!x.IsString()) {
- return Status::Invalid("dim_names must contain strings, got ",
- internal::JsonTypeName(x));
- }
- dim_names.emplace_back(x.GetString());
- }
+ if (dim_names_value.has_value()) {
+ ARROW_ASSIGN_OR_RAISE(dim_names,
+ internal::GetJsonStringArray(*dim_names_value,
"dim_names"));
+
if (shape.size() != dim_names.size()) {
return Status::Invalid("Invalid dim_names");
}
@@ -177,14 +158,18 @@ Result<std::shared_ptr<DataType>>
FixedShapeTensorType::Deserialize(
// (type mismatches, size mismatches) are reported first.
ARROW_ASSIGN_OR_RAISE(auto ext_type, FixedShapeTensorType::Make(
value_type, shape, permutation,
dim_names));
+
const auto& fst_type = internal::checked_cast<const
FixedShapeTensorType&>(*ext_type);
+
ARROW_ASSIGN_OR_RAISE(const int64_t expected_size,
internal::ComputeShapeProduct(fst_type.shape()));
+
if (expected_size != fsl_type->list_size()) {
return Status::Invalid("Product of shape dimensions (", expected_size,
") does not match FixedSizeList size (",
fsl_type->list_size(),
")");
}
+
return ext_type;
}
diff --git a/cpp/src/arrow/extension/tensor_extension_array_test.cc
b/cpp/src/arrow/extension/tensor_extension_array_test.cc
index 531fc3c01cf..29147df6ba6 100644
--- a/cpp/src/arrow/extension/tensor_extension_array_test.cc
+++ b/cpp/src/arrow/extension/tensor_extension_array_test.cc
@@ -223,15 +223,15 @@ TEST_F(TestFixedShapeTensorType,
MetadataSerializationRoundtrip) {
// Validate shape values must be integers. Error message should include the
// JSON type name of the offending value.
CheckDeserializationRaises(ext_type_, storage_type, R"({"shape":[3.5,4]})",
- "shape must contain integers, got Number");
+ "shape must contain integers, got number");
CheckDeserializationRaises(ext_type_, storage_type, R"({"shape":["3","4"]})",
- "shape must contain integers, got String");
+ "shape must contain integers, got string");
CheckDeserializationRaises(ext_type_, storage_type, R"({"shape":[null]})",
- "shape must contain integers, got Null");
+ "shape must contain integers, got null");
CheckDeserializationRaises(ext_type_, storage_type, R"({"shape":[true]})",
- "shape must contain integers, got True");
+ "shape must contain integers, got boolean");
CheckDeserializationRaises(ext_type_, storage_type, R"({"shape":[false]})",
- "shape must contain integers, got False");
+ "shape must contain integers, got boolean");
// Validate shape values must be non-negative
CheckDeserializationRaises(ext_type_, fixed_size_list(int64(), 1),
R"({"shape":[-1]})",
@@ -244,16 +244,20 @@ TEST_F(TestFixedShapeTensorType,
MetadataSerializationRoundtrip) {
// Validate permutation member must be an array with integer values
CheckDeserializationRaises(ext_type_, storage_type,
R"({"shape":[3,4],"permutation":"invalid"})",
- "permutation must be an array, got String");
+ "permutation must be an array, got string");
CheckDeserializationRaises(ext_type_, storage_type,
R"({"shape":[3,4],"permutation":{"a":1}})",
- "permutation must be an array, got Object");
+ "permutation must be an array, got object");
CheckDeserializationRaises(ext_type_, storage_type,
R"({"shape":[3,4],"permutation":[1.5,0.5]})",
- "permutation must contain integers, got Number");
+ "permutation must contain integers, got number");
CheckDeserializationRaises(ext_type_, storage_type,
R"({"shape":[3,4],"permutation":["a","b"]})",
- "permutation must contain integers, got String");
+ "permutation must contain integers, got string");
+ // Validate permutation member must be an array with integer values
+ CheckDeserializationRaises(ext_type_, storage_type,
+ R"({"shape":[3,4],"permutation":[]})",
+ "Invalid permutation");
// Validate permutation values must be unique integers in [0, N-1]
CheckDeserializationRaises(ext_type_, storage_type,
@@ -269,13 +273,15 @@ TEST_F(TestFixedShapeTensorType,
MetadataSerializationRoundtrip) {
// Validate dim_names member must be an array with string values
CheckDeserializationRaises(ext_type_, storage_type,
R"({"shape":[3,4],"dim_names":"invalid"})",
- "dim_names must be an array, got String");
+ "dim_names must be an array, got string");
CheckDeserializationRaises(ext_type_, storage_type,
R"({"shape":[3,4],"dim_names":[1,2]})",
- "dim_names must contain strings, got Number");
+ "dim_names must contain strings, got number");
CheckDeserializationRaises(ext_type_, storage_type,
R"({"shape":[3,4],"dim_names":[null,null]})",
- "dim_names must contain strings, got Null");
+ "dim_names must contain strings, got null");
+ CheckDeserializationRaises(ext_type_, storage_type,
R"({"shape":[3,4],"dim_names":[]})",
+ "Invalid dim_names");
}
TEST_F(TestFixedShapeTensorType, MakeValidatesShape) {
@@ -858,35 +864,41 @@ TEST_F(TestVariableShapeTensorType,
MetadataSerializationRoundtrip) {
CheckDeserializationRaises(ext_type_, storage_type, R"({"shape":(3,4)})",
"Invalid serialized JSON data");
CheckDeserializationRaises(ext_type_, storage_type,
R"({"permutation":[1,0]})",
- "Invalid: permutation");
+ "Invalid permutation");
CheckDeserializationRaises(ext_type_, storage_type,
R"({"dim_names":["x","y"]})",
- "Invalid: dim_names");
+ "Invalid dim_names");
// Validate permutation member must be an array with integer values. Error
// message should include the JSON type name of the offending value.
CheckDeserializationRaises(ext_type_, storage_type,
R"({"permutation":"invalid"})",
- "permutation must be an array, got String");
+ "permutation must be an array, got string");
CheckDeserializationRaises(ext_type_, storage_type,
R"({"permutation":[1.5,0.5,2.5]})",
- "permutation must contain integers, got Number");
+ "permutation must contain integers, got number");
CheckDeserializationRaises(ext_type_, storage_type,
R"({"permutation":[null,null,null]})",
- "permutation must contain integers, got Null");
+ "permutation must contain integers, got null");
+ CheckDeserializationRaises(ext_type_, storage_type, R"({"permutation":[]})",
+ "Invalid permutation");
// Validate dim_names member must be an array with string values
CheckDeserializationRaises(ext_type_, storage_type,
R"({"dim_names":"invalid"})",
- "dim_names must be an array, got String");
+ "dim_names must be an array, got string");
CheckDeserializationRaises(ext_type_, storage_type,
R"({"dim_names":[1,2,3]})",
- "dim_names must contain strings, got Number");
+ "dim_names must contain strings, got number");
+ CheckDeserializationRaises(ext_type_, storage_type, R"({"dim_names":[]})",
+ "Invalid dim_names");
// Validate uniform_shape member must be an array with integer-or-null values
CheckDeserializationRaises(ext_type_, storage_type,
R"({"uniform_shape":"invalid"})",
- "uniform_shape must be an array, got String");
+ "uniform_shape must be an array, got string");
CheckDeserializationRaises(ext_type_, storage_type,
R"({"uniform_shape":[1.5,null,null]})",
- "uniform_shape must contain integers or nulls,
got Number");
+ "uniform_shape must contain integers or nulls,
got number");
CheckDeserializationRaises(ext_type_, storage_type,
R"({"uniform_shape":["x",null,null]})",
- "uniform_shape must contain integers or nulls,
got String");
+ "uniform_shape must contain integers or nulls,
got string");
+ CheckDeserializationRaises(ext_type_, storage_type,
R"({"uniform_shape":[]})",
+ "Invalid uniform_shape");
}
TEST_F(TestVariableShapeTensorType, RoundtripBatch) {
diff --git a/cpp/src/arrow/extension/tensor_internal.cc
b/cpp/src/arrow/extension/tensor_internal.cc
index e94ea9a1d18..d2965bc578f 100644
--- a/cpp/src/arrow/extension/tensor_internal.cc
+++ b/cpp/src/arrow/extension/tensor_internal.cc
@@ -30,20 +30,6 @@
namespace arrow::internal {
-namespace {
-
-// Names indexed by rapidjson::Type enum value:
-// kNullType=0, kFalseType=1, kTrueType=2, kObjectType=3,
-// kArrayType=4, kStringType=5, kNumberType=6.
-constexpr const char* kJsonTypeNames[] = {"Null", "False", "True", "Object",
- "Array", "String", "Number"};
-
-} // namespace
-
-const char* JsonTypeName(const ::arrow::rapidjson::Value& v) {
- return kJsonTypeNames[v.GetType()];
-}
-
Result<int64_t> ComputeShapeProduct(std::span<const int64_t> shape) {
int64_t product = 1;
for (const auto dim : shape) {
diff --git a/cpp/src/arrow/extension/tensor_internal.h
b/cpp/src/arrow/extension/tensor_internal.h
index 19665bf2cd4..b54945ad50a 100644
--- a/cpp/src/arrow/extension/tensor_internal.h
+++ b/cpp/src/arrow/extension/tensor_internal.h
@@ -21,18 +21,11 @@
#include <span>
#include <vector>
-#include "arrow/json/rapidjson_defs.h" // IWYU pragma: keep
#include "arrow/result.h"
#include "arrow/type_fwd.h"
-#include <rapidjson/document.h>
-
namespace arrow::internal {
-/// \brief Return the name of a RapidJSON value's type (e.g., "Null", "Array",
"Number").
-ARROW_EXPORT
-const char* JsonTypeName(const ::arrow::rapidjson::Value& v);
-
/// \brief Compute the product of the given shape dimensions.
///
/// Returns Status::Invalid if the product would overflow int64_t.
diff --git a/cpp/src/arrow/extension/variable_shape_tensor.cc
b/cpp/src/arrow/extension/variable_shape_tensor.cc
index 40171f909a9..784cd334b1d 100644
--- a/cpp/src/arrow/extension/variable_shape_tensor.cc
+++ b/cpp/src/arrow/extension/variable_shape_tensor.cc
@@ -17,22 +17,21 @@
#include <sstream>
+#include <simdjson.h>
+
#include "arrow/extension/tensor_internal.h"
#include "arrow/extension/variable_shape_tensor.h"
#include "arrow/array/array_primitive.h"
#include "arrow/json/json_writer_internal.h"
-#include "arrow/json/rapidjson_defs.h" // IWYU pragma: keep
#include "arrow/scalar.h"
#include "arrow/tensor.h"
#include "arrow/util/logging_internal.h"
#include "arrow/util/print_internal.h"
+#include "arrow/util/simdjson_internal.h"
#include "arrow/util/sort_internal.h"
#include "arrow/util/string.h"
-#include <rapidjson/document.h>
-
-namespace rj = arrow::rapidjson;
using ::arrow::json::JsonWriter;
namespace arrow::extension {
@@ -155,63 +154,46 @@ Result<std::shared_ptr<DataType>>
VariableShapeTensorType::Deserialize(
internal::checked_cast<const
FixedSizeListType&>(*storage_type->field(1)->type())
.list_size();
- rj::Document document;
- if (document.Parse(serialized_data.data(),
serialized_data.length()).HasParseError() ||
- !document.IsObject()) {
- return Status::Invalid("Invalid serialized JSON data: ", serialized_data);
- }
+ simdjson::dom::parser parser;
+ ARROW_ASSIGN_OR_RAISE(auto object, internal::ParseJsonObject(parser,
serialized_data));
+
+ ARROW_ASSIGN_OR_RAISE(auto permutation_value,
+ internal::GetOptionalJsonField(object, "permutation"));
std::vector<int64_t> permutation;
- if (document.HasMember("permutation")) {
- const auto& json_permutation = document["permutation"];
- if (!json_permutation.IsArray()) {
- return Status::Invalid("permutation must be an array, got ",
- internal::JsonTypeName(json_permutation));
- }
- permutation.reserve(ndim);
- for (const auto& x : json_permutation.GetArray()) {
- if (!x.IsInt64()) {
- return Status::Invalid("permutation must contain integers, got ",
- internal::JsonTypeName(x));
- }
- permutation.emplace_back(x.GetInt64());
+ if (permutation_value.has_value()) {
+ ARROW_ASSIGN_OR_RAISE(permutation,
+ internal::GetJsonIntArray(*permutation_value,
"permutation"));
+
+ if (permutation.size() != static_cast<size_t>(ndim)) {
+ return Status::Invalid("Invalid permutation");
}
RETURN_NOT_OK(internal::IsPermutationValid(permutation));
}
+
+ ARROW_ASSIGN_OR_RAISE(auto dim_names_value,
+ internal::GetOptionalJsonField(object, "dim_names"));
+
std::vector<std::string> dim_names;
- if (document.HasMember("dim_names")) {
- const auto& json_dim_names = document["dim_names"];
- if (!json_dim_names.IsArray()) {
- return Status::Invalid("dim_names must be an array, got ",
- internal::JsonTypeName(json_dim_names));
- }
- dim_names.reserve(ndim);
- for (const auto& x : json_dim_names.GetArray()) {
- if (!x.IsString()) {
- return Status::Invalid("dim_names must contain strings, got ",
- internal::JsonTypeName(x));
- }
- dim_names.emplace_back(x.GetString());
+ if (dim_names_value.has_value()) {
+ ARROW_ASSIGN_OR_RAISE(dim_names,
+ internal::GetJsonStringArray(*dim_names_value,
"dim_names"));
+
+ if (dim_names.size() != static_cast<size_t>(ndim)) {
+ return Status::Invalid("Invalid dim_names");
}
}
+ ARROW_ASSIGN_OR_RAISE(auto uniform_shape_value,
+ internal::GetOptionalJsonField(object,
"uniform_shape"));
+
std::vector<std::optional<int64_t>> uniform_shape;
- if (document.HasMember("uniform_shape")) {
- const auto& json_uniform_shape = document["uniform_shape"];
- if (!json_uniform_shape.IsArray()) {
- return Status::Invalid("uniform_shape must be an array, got ",
- internal::JsonTypeName(json_uniform_shape));
- }
- uniform_shape.reserve(ndim);
- for (const auto& x : json_uniform_shape.GetArray()) {
- if (x.IsNull()) {
- uniform_shape.emplace_back(std::nullopt);
- } else if (x.IsInt64()) {
- uniform_shape.emplace_back(x.GetInt64());
- } else {
- return Status::Invalid("uniform_shape must contain integers or nulls,
got ",
- internal::JsonTypeName(x));
- }
+ if (uniform_shape_value.has_value()) {
+ ARROW_ASSIGN_OR_RAISE(uniform_shape, internal::GetJsonNullableIntArray(
+ *uniform_shape_value,
"uniform_shape"));
+
+ if (uniform_shape.size() != static_cast<size_t>(ndim)) {
+ return Status::Invalid("Invalid uniform_shape");
}
}
diff --git a/cpp/src/arrow/util/simdjson_internal.h
b/cpp/src/arrow/util/simdjson_internal.h
index 1badd99938b..799ebfc5ea6 100644
--- a/cpp/src/arrow/util/simdjson_internal.h
+++ b/cpp/src/arrow/util/simdjson_internal.h
@@ -19,8 +19,11 @@
#include <concepts>
#include <cstdint>
+#include <optional>
+#include <string>
#include <string_view>
#include <utility>
+#include <vector>
#include <simdjson.h>
@@ -91,6 +94,122 @@ Result<T>
ResolveSimdjsonResult(simdjson::simdjson_result<T> result,
return value;
}
+inline const char* JsonTypeName(simdjson::dom::element_type type) {
+ switch (type) {
+ case simdjson::dom::element_type::ARRAY:
+ return "array";
+ case simdjson::dom::element_type::OBJECT:
+ return "object";
+ case simdjson::dom::element_type::INT64:
+ case simdjson::dom::element_type::UINT64:
+ case simdjson::dom::element_type::DOUBLE:
+ return "number";
+ case simdjson::dom::element_type::STRING:
+ return "string";
+ case simdjson::dom::element_type::BOOL:
+ return "boolean";
+ case simdjson::dom::element_type::NULL_VALUE:
+ return "null";
+ default:
+ return "unknown";
+ }
+}
+
+inline Result<simdjson::dom::array> GetJsonArray(simdjson::dom::element value,
+ std::string_view name) {
+ if (!value.is_array()) {
+ return Status::Invalid(name, " must be an array, got ",
JsonTypeName(value.type()));
+ }
+ return ResolveSimdjsonResult(value.get_array(), "Failed to get JSON array");
+}
+
+inline Result<int64_t> GetJsonInt(simdjson::dom::element value,
std::string_view name,
+ std::string_view expected) {
+ if (!value.is_int64()) {
+ return Status::Invalid(name, " must contain ", expected, ", got ",
+ JsonTypeName(value.type()));
+ }
+ return ResolveSimdjsonResult(value.get_int64(), "Failed to get JSON
integer");
+}
+
+inline Result<simdjson::dom::object> ParseJsonObject(simdjson::dom::parser&
parser,
+ const std::string& json) {
+ return ResolveSimdjsonResult(parser.parse(json).get_object(),
+ "Invalid serialized JSON data");
+}
+
+// object.at_key() performs a linear search. This is acceptable here
+// since these objects are expected to contain only a small number of fields.
+inline Result<std::optional<simdjson::dom::element>> GetOptionalJsonField(
+ const simdjson::dom::object& object, std::string_view key) {
+ auto field = object.at_key(key);
+ if (field.error() == simdjson::NO_SUCH_FIELD) {
+ return std::nullopt;
+ }
+
+ ARROW_ASSIGN_OR_RAISE(
+ auto value,
+ ResolveSimdjsonResult(std::move(field), "Failed to get JSON object
field"));
+
+ return std::optional<simdjson::dom::element>(std::move(value));
+}
+
+inline Result<std::vector<int64_t>> GetJsonIntArray(simdjson::dom::element
value,
+ std::string_view name) {
+ ARROW_ASSIGN_OR_RAISE(auto array, GetJsonArray(value, name));
+
+ std::vector<int64_t> result;
+ result.reserve(array.size());
+
+ for (auto element : array) {
+ ARROW_ASSIGN_OR_RAISE(auto number, GetJsonInt(element, name, "integers"));
+ result.push_back(number);
+ }
+
+ return result;
+}
+
+inline Result<std::vector<std::optional<int64_t>>> GetJsonNullableIntArray(
+ simdjson::dom::element value, std::string_view name) {
+ ARROW_ASSIGN_OR_RAISE(auto array, GetJsonArray(value, name));
+
+ std::vector<std::optional<int64_t>> result;
+ result.reserve(array.size());
+
+ for (auto element : array) {
+ if (element.is_null()) {
+ result.emplace_back(std::nullopt);
+ } else {
+ ARROW_ASSIGN_OR_RAISE(auto number, GetJsonInt(element, name, "integers
or nulls"));
+ result.emplace_back(number);
+ }
+ }
+
+ return result;
+}
+
+inline Result<std::vector<std::string>>
GetJsonStringArray(simdjson::dom::element value,
+ std::string_view
name) {
+ ARROW_ASSIGN_OR_RAISE(auto array, GetJsonArray(value, name));
+
+ std::vector<std::string> result;
+ result.reserve(array.size());
+
+ for (auto element : array) {
+ if (!element.is_string()) {
+ return Status::Invalid(name, " must contain strings, got ",
+ JsonTypeName(element.type()));
+ }
+
+ ARROW_ASSIGN_OR_RAISE(
+ auto string,
+ ResolveSimdjsonResult(element.get_string(), "Failed to get JSON
string"));
+ result.emplace_back(string);
+ }
+
+ return result;
+}
+
template <typename ObjectFn, typename ArrayFn, typename StringFn, typename
BoolFn,
typename NullFn, typename Int64Fn, typename Uint64Fn, typename
DoubleFn,
typename BigIntegerFn>