Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 4 additions & 1 deletion cpp/src/arrow/extension/fixed_shape_tensor.cc
Original file line number Diff line number Diff line change
Expand Up @@ -104,7 +104,10 @@ std::string FixedShapeTensorType::Serialize() const {

writer.EndObject();

return std::string(writer.GetString());
Result<std::string_view> json = writer.GetString();
// can only fail in OutOfMemory scenarios
ARROW_CHECK_OK(json.status());
return std::string(*json);
}

Result<std::shared_ptr<DataType>> FixedShapeTensorType::Deserialize(
Expand Down
5 changes: 4 additions & 1 deletion cpp/src/arrow/extension/opaque.cc
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,10 @@ std::string OpaqueType::Serialize() const {

writer.EndObject();

return std::string(writer.GetString());
Result<std::string_view> json = writer.GetString();
// can only fail in OutOfMemory scenarios
ARROW_CHECK_OK(json.status());
return std::string(*json);
}

Result<std::shared_ptr<DataType>> OpaqueType::Deserialize(
Expand Down
5 changes: 4 additions & 1 deletion cpp/src/arrow/extension/variable_shape_tensor.cc
Original file line number Diff line number Diff line change
Expand Up @@ -120,7 +120,10 @@ std::string VariableShapeTensorType::Serialize() const {

writer.EndObject();

return std::string(writer.GetString());
Result<std::string_view> json = writer.GetString();
// can only fail in OutOfMemory scenarios
ARROW_CHECK_OK(json.status());
return std::string(*json);
}

Result<std::shared_ptr<DataType>> VariableShapeTensorType::Deserialize(
Expand Down
3 changes: 2 additions & 1 deletion cpp/src/arrow/integration/json_integration.cc
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,8 @@ class IntegrationJsonWriter::Impl {
writer_.EndArray(); // Record batches
writer_.EndObject();

return std::string(writer_.GetString());
ARROW_ASSIGN_OR_RAISE(std::string_view json, writer_.GetString());
return std::string(json);
}

Status WriteRecordBatch(const RecordBatch& batch) {
Expand Down
4 changes: 2 additions & 2 deletions cpp/src/arrow/integration/json_integration_test.cc
Original file line number Diff line number Diff line change
Expand Up @@ -732,7 +732,7 @@ void TestSchemaRoundTrip(const std::shared_ptr<Schema>& schema) {
ASSERT_OK(json::WriteSchema(*schema, mapper, &writer));
writer.EndObject();

std::string json_schema(writer.GetString());
ASSERT_OK_AND_ASSIGN(std::string_view json_schema, writer.GetString());

rj::Document d;
// Pass explicit size to avoid ASAN issues with
Expand All @@ -752,7 +752,7 @@ void TestArrayRoundTrip(const Array& array) {

ASSERT_OK(json::WriteArray(name, array, &writer));

std::string array_as_json(writer.GetString());
ASSERT_OK_AND_ASSIGN(std::string_view array_as_json, writer.GetString());

rj::Document d;
// Pass explicit size to avoid ASAN issues with
Expand Down
55 changes: 40 additions & 15 deletions cpp/src/arrow/json/from_string.cc
Original file line number Diff line number Diff line change
Expand Up @@ -166,6 +166,16 @@ Result<SimdjsonValueType> GetJsonResult(
return typed_value;
}

// Result<bool> because peeking the nonRootScalar can fail (parsed lazily)
Result<bool> IsJsonNull(sj::value& value) {
bool is_null;
if (auto error_code = value.is_null().get(is_null); error_code != simdjson::SUCCESS) {
return Status::Invalid("Error checking for JSON null: ",
simdjson::error_message(error_code));
}
return is_null;
}

class JSONConverter {
public:
virtual ~JSONConverter() = default;
Expand Down Expand Up @@ -262,7 +272,8 @@ class BooleanConverter final : public ConcreteConverter<BooleanConverter> {
}

Status AppendValue(sj::value& json_obj) override {
if (json_obj.is_null()) {
ARROW_ASSIGN_OR_RAISE(bool is_null, IsJsonNull(json_obj));
if (is_null) {
return AppendNull();
}
int64_t int_value;
Expand Down Expand Up @@ -415,7 +426,8 @@ class IntegerConverter final
Status Init() override { return this->MakeConcreteBuilder(&builder_); }

Status AppendValue(sj::value& json_obj) override {
if (json_obj.is_null()) {
ARROW_ASSIGN_OR_RAISE(bool is_null, IsJsonNull(json_obj));
if (is_null) {
return this->AppendNull();
}
c_type value;
Expand All @@ -442,7 +454,8 @@ class FloatConverter final : public ConcreteConverter<FloatConverter<Type, Build
Status Init() override { return this->MakeConcreteBuilder(&builder_); }

Status AppendValue(sj::value& json_obj) override {
if (json_obj.is_null()) {
ARROW_ASSIGN_OR_RAISE(bool is_null, IsJsonNull(json_obj));
if (is_null) {
return this->AppendNull();
}
c_type value;
Expand Down Expand Up @@ -472,7 +485,8 @@ class DecimalConverter final
Status Init() override { return this->MakeConcreteBuilder(&builder_); }

Status AppendValue(sj::value& json_obj) override {
if (json_obj.is_null()) {
ARROW_ASSIGN_OR_RAISE(bool is_null, IsJsonNull(json_obj));
if (is_null) {
return this->AppendNull();
}
ARROW_ASSIGN_OR_RAISE(auto string_value, GetJsonAs<std::string_view>(json_obj));
Expand Down Expand Up @@ -514,7 +528,8 @@ class TimestampConverter final : public ConcreteConverter<TimestampConverter> {
}

Status AppendValue(sj::value& json_obj) override {
if (json_obj.is_null()) {
ARROW_ASSIGN_OR_RAISE(bool is_null, IsJsonNull(json_obj));
if (is_null) {
return this->AppendNull();
}
int64_t value;
Expand Down Expand Up @@ -548,7 +563,8 @@ class DayTimeIntervalConverter final
}

Status AppendValue(sj::value& json_obj) override {
if (json_obj.is_null()) {
ARROW_ASSIGN_OR_RAISE(bool is_null, IsJsonNull(json_obj));
if (is_null) {
return this->AppendNull();
}

Expand Down Expand Up @@ -581,7 +597,8 @@ class MonthDayNanoIntervalConverter final
}

Status AppendValue(sj::value& json_obj) override {
if (json_obj.is_null()) {
ARROW_ASSIGN_OR_RAISE(bool is_null, IsJsonNull(json_obj));
if (is_null) {
return this->AppendNull();
}

Expand Down Expand Up @@ -620,7 +637,8 @@ class StringConverter final
Status Init() override { return this->MakeConcreteBuilder(&builder_); }

Status AppendValue(sj::value& json_obj) override {
if (json_obj.is_null()) {
ARROW_ASSIGN_OR_RAISE(bool is_null, IsJsonNull(json_obj));
if (is_null) {
return this->AppendNull();
}

Expand Down Expand Up @@ -648,7 +666,8 @@ class FixedSizeBinaryConverter final
Status Init() override { return this->MakeConcreteBuilder(&builder_); }

Status AppendValue(sj::value& json_obj) override {
if (json_obj.is_null()) {
ARROW_ASSIGN_OR_RAISE(bool is_null, IsJsonNull(json_obj));
if (is_null) {
return this->AppendNull();
}
ARROW_ASSIGN_OR_RAISE(auto view, GetJsonAs<std::string_view>(json_obj));
Expand Down Expand Up @@ -691,7 +710,8 @@ class VarLengthListLikeConverter final
}

Status AppendValue(sj::value& json_obj) override {
if (json_obj.is_null()) {
ARROW_ASSIGN_OR_RAISE(bool is_null, IsJsonNull(json_obj));
if (is_null) {
return this->AppendNull();
}
ARROW_ASSIGN_OR_RAISE(auto array, GetJsonAs<sj::array>(json_obj));
Expand Down Expand Up @@ -730,7 +750,8 @@ class MapConverter final : public ConcreteConverter<MapConverter> {
}

Status AppendValue(sj::value& json_obj) override {
if (json_obj.is_null()) {
ARROW_ASSIGN_OR_RAISE(bool is_null, IsJsonNull(json_obj));
if (is_null) {
return this->AppendNull();
}
RETURN_NOT_OK(builder_->Append());
Expand All @@ -746,7 +767,8 @@ class MapConverter final : public ConcreteConverter<MapConverter> {
RETURN_NOT_OK(ProcessJsonArrayElements<2>(
json_pair_array, "key-item pair",
{[this](sj::value& key) {
if (key.is_null()) {
ARROW_ASSIGN_OR_RAISE(bool key_is_null, IsJsonNull(key));
if (key_is_null) {
return Status::Invalid("null key is invalid");
}
return key_converter_->AppendValue(key);
Expand Down Expand Up @@ -781,7 +803,8 @@ class FixedSizeListConverter final : public ConcreteConverter<FixedSizeListConve
}

Status AppendValue(sj::value& json_obj) override {
if (json_obj.is_null()) {
ARROW_ASSIGN_OR_RAISE(bool is_null, IsJsonNull(json_obj));
if (is_null) {
return this->AppendNull();
}
RETURN_NOT_OK(builder_->Append());
Expand Down Expand Up @@ -829,7 +852,8 @@ class StructConverter final : public ConcreteConverter<StructConverter> {
// or an object mapping struct names to values (omitted struct members
// are mapped to null).
Status AppendValue(sj::value& json_obj) override {
if (json_obj.is_null()) {
ARROW_ASSIGN_OR_RAISE(bool is_null, IsJsonNull(json_obj));
if (is_null) {
return this->AppendNull();
}
sj::array array;
Expand Down Expand Up @@ -937,7 +961,8 @@ 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(sj::value& json_obj) override {
if (json_obj.is_null()) {
ARROW_ASSIGN_OR_RAISE(bool is_null, IsJsonNull(json_obj));
if (is_null) {
return this->AppendNull();
}

Expand Down
13 changes: 12 additions & 1 deletion cpp/src/arrow/json/json_writer_internal.cc
Original file line number Diff line number Diff line change
Expand Up @@ -102,7 +102,18 @@ void JsonWriter::Null() {
needs_comma_ = true;
}

std::string_view JsonWriter::GetString() const { return builder_.view().value(); }
Result<std::string_view> JsonWriter::GetString() const {
std::string_view view;
if (auto error = builder_.view().get(view); error != simdjson::SUCCESS) {
if (error == simdjson::OUT_OF_CAPACITY) {
return Status::OutOfMemory(
"OutOfMemory when allocating buffer to serialize json to string");
}
return Status::Invalid("Failed to retrieve json from string builder: ",
simdjson::error_message(error));
}
return view;
}

void JsonWriter::Clear() {
builder_.clear();
Expand Down
3 changes: 2 additions & 1 deletion cpp/src/arrow/json/json_writer_internal.h
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
#include <cstdint>
#include <string_view>

#include "arrow/result.h"
#include "arrow/util/visibility.h"

namespace arrow::json {
Expand Down Expand Up @@ -55,7 +56,7 @@ class ARROW_EXPORT JsonWriter {
void StringField(std::string_view key, std::string_view value);
void BoolField(std::string_view key, bool value);

std::string_view GetString() const;
Result<std::string_view> GetString() const;

void Clear();

Expand Down
41 changes: 31 additions & 10 deletions cpp/src/arrow/json/json_writer_internal_test.cc
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
#include <gtest/gtest.h>

#include "arrow/json/json_writer_internal.h"
#include "arrow/testing/gtest_util.h"

namespace arrow::json {

Expand All @@ -31,7 +32,9 @@ TEST(JsonWriter, SimpleObject) {
writer.String("hello");
writer.EndObject();

EXPECT_EQ(writer.GetString(), R"({"a":42,"b":"hello"})");
ASSERT_OK_AND_ASSIGN(std::string_view json, writer.GetString());

EXPECT_EQ(json, R"({"a":42,"b":"hello"})");
}

TEST(JsonWriter, Array) {
Expand All @@ -43,7 +46,9 @@ TEST(JsonWriter, Array) {
writer.Int(3);
writer.EndArray();

EXPECT_EQ(writer.GetString(), "[1,2,3]");
ASSERT_OK_AND_ASSIGN(std::string_view json, writer.GetString());

EXPECT_EQ(json, "[1,2,3]");
}

TEST(JsonWriter, NestedObject) {
Expand All @@ -59,7 +64,9 @@ TEST(JsonWriter, NestedObject) {

writer.EndObject();

EXPECT_EQ(writer.GetString(), R"({"child":{"x":true}})");
ASSERT_OK_AND_ASSIGN(std::string_view json, writer.GetString());

EXPECT_EQ(json, R"({"child":{"x":true}})");
}

TEST(JsonWriter, NullValue) {
Expand All @@ -70,7 +77,9 @@ TEST(JsonWriter, NullValue) {
writer.Null();
writer.EndObject();

EXPECT_EQ(writer.GetString(), R"({"value":null})");
ASSERT_OK_AND_ASSIGN(std::string_view json, writer.GetString());

EXPECT_EQ(json, R"({"value":null})");
}

TEST(JsonWriter, DoubleValue) {
Expand All @@ -81,7 +90,9 @@ TEST(JsonWriter, DoubleValue) {
writer.Double(3.14);
writer.EndObject();

EXPECT_EQ(writer.GetString(), R"({"pi":3.14})");
ASSERT_OK_AND_ASSIGN(std::string_view json, writer.GetString());

EXPECT_EQ(json, R"({"pi":3.14})");
}

TEST(JsonWriter, UnsignedValues) {
Expand All @@ -94,7 +105,9 @@ TEST(JsonWriter, UnsignedValues) {
writer.Uint64(1234567890123ULL);
writer.EndObject();

EXPECT_EQ(writer.GetString(), R"({"u32":42,"u64":1234567890123})");
ASSERT_OK_AND_ASSIGN(std::string_view json, writer.GetString());

EXPECT_EQ(json, R"({"u32":42,"u64":1234567890123})");
}

TEST(JsonWriter, Int64Value) {
Expand All @@ -105,7 +118,9 @@ TEST(JsonWriter, Int64Value) {
writer.Int64(-1234567890123LL);
writer.EndObject();

EXPECT_EQ(writer.GetString(), R"({"i64":-1234567890123})");
ASSERT_OK_AND_ASSIGN(std::string_view json, writer.GetString());

EXPECT_EQ(json, R"({"i64":-1234567890123})");
}

TEST(JsonWriter, Clear) {
Expand All @@ -122,7 +137,9 @@ TEST(JsonWriter, Clear) {
writer.Int(5);
writer.EndArray();

EXPECT_EQ(writer.GetString(), "[5]");
ASSERT_OK_AND_ASSIGN(std::string_view json, writer.GetString());

EXPECT_EQ(json, "[5]");
}

TEST(JsonWriter, RawValue) {
Expand All @@ -133,7 +150,9 @@ TEST(JsonWriter, RawValue) {
writer.RawValue("123.456");
writer.EndObject();

ASSERT_EQ(writer.GetString(), R"({"number":123.456})");
ASSERT_OK_AND_ASSIGN(std::string_view json, writer.GetString());

EXPECT_EQ(json, R"({"number":123.456})");
}

TEST(JsonWriter, StringWithExplicitLength) {
Expand All @@ -146,7 +165,9 @@ TEST(JsonWriter, StringWithExplicitLength) {
writer.String(std::string_view(value, 3));
writer.EndObject();

ASSERT_EQ(writer.GetString(), R"({"value":"abc"})");
ASSERT_OK_AND_ASSIGN(std::string_view json, writer.GetString());

EXPECT_EQ(json, R"({"value":"abc"})");
}

} // namespace arrow::json
Original file line number Diff line number Diff line change
Expand Up @@ -87,7 +87,8 @@ std::string FileSystemKeyMaterialStore::BuildKeyMaterialMapJson() {
writer.StringField(it.first, it.second);
}
writer.EndObject();
return std::string(writer.GetString());
PARQUET_ASSIGN_OR_THROW(std::string_view json, writer.GetString());
return std::string(json);
}

void FileSystemKeyMaterialStore::SaveMaterial() {
Expand Down
Loading
Loading