diff --git a/cpp/src/arrow/json/parser.cc b/cpp/src/arrow/json/parser.cc index 69373d07fb6..08d83f0c67f 100644 --- a/cpp/src/arrow/json/parser.cc +++ b/cpp/src/arrow/json/parser.cc @@ -526,16 +526,15 @@ class RawBuilderSet { /// Appending null is slightly tricky since null count is stored inline /// for builders of Kind::kNull. Append nulls using this helper - Status AppendNull(BuilderPtr parent, int field_index, BuilderPtr builder) { + Result AppendNull(BuilderPtr parent, int field_index) { + BuilderPtr builder = parent.kind == Kind::kArray + ? Cast(parent)->value_builder() + : Cast(parent)->field_builder(field_index); if (ARROW_PREDICT_FALSE(!builder.nullable)) { return ParseError("a required field was null"); } switch (builder.kind) { case Kind::kNull: { - DCHECK_EQ(builder, parent.kind == Kind::kArray - ? Cast(parent)->value_builder() - : Cast(parent)->field_builder(field_index)); - // increment null count stored inline builder.index += 1; @@ -545,38 +544,43 @@ class RawBuilderSet { } else { Cast(parent)->field_builder(field_index, builder); } - return Status::OK(); + break; } case Kind::kBoolean: - return Cast(builder)->AppendNull(); + RETURN_NOT_OK(Cast(builder)->AppendNull()); + break; case Kind::kNumber: - return Cast(builder)->AppendNull(); + RETURN_NOT_OK(Cast(builder)->AppendNull()); + break; case Kind::kString: - return Cast(builder)->AppendNull(); + RETURN_NOT_OK(Cast(builder)->AppendNull()); + break; case Kind::kNumberOrString: { - return Cast(builder)->AppendNull(); + RETURN_NOT_OK(Cast(builder)->AppendNull()); + break; } case Kind::kArray: - return Cast(builder)->AppendNull(); + RETURN_NOT_OK(Cast(builder)->AppendNull()); + break; case Kind::kObject: { auto struct_builder = Cast(builder); RETURN_NOT_OK(struct_builder->AppendNull()); for (int i = 0; i < struct_builder->num_fields(); ++i) { - auto field_builder = struct_builder->field_builder(i); - RETURN_NOT_OK(AppendNull(builder, i, field_builder)); + RETURN_NOT_OK(AppendNull(builder, i)); } - return Status::OK(); + break; } default: return Status::NotImplemented("invalid builder Kind"); } + return builder; } Status Finish(const std::shared_ptr& scalar_values, BuilderPtr builder, @@ -669,7 +673,9 @@ class ParseImpl : public BlockParser { Status Null() { DCHECK(!builder_stack_.empty()); - return builder_set_.AppendNull(builder_stack_.back(), field_index_, builder_); + ARROW_ASSIGN_OR_RAISE(builder_, + builder_set_.AppendNull(builder_stack_.back(), field_index_)); + return Status::OK(); } Status HandleUnexpectedField(std::string_view key, sj::value& value) { @@ -962,11 +968,10 @@ class ParseImpl : public BlockParser { if (!absent_fields_stack_[i]) { continue; } - auto field_builder = Cast(parent)->field_builder(i); - if (ARROW_PREDICT_FALSE(!field_builder.nullable)) { + if (ARROW_PREDICT_FALSE(!Cast(parent)->field_builder(i).nullable)) { return ParseError("a required field was absent"); } - RETURN_NOT_OK(builder_set_.AppendNull(parent, i, field_builder)); + RETURN_NOT_OK(builder_set_.AppendNull(parent, i)); } absent_fields_stack_.Pop(); EndNested(); diff --git a/cpp/src/arrow/json/parser_test.cc b/cpp/src/arrow/json/parser_test.cc index 6c32fd7cab3..ae764c71804 100644 --- a/cpp/src/arrow/json/parser_test.cc +++ b/cpp/src/arrow/json/parser_test.cc @@ -236,6 +236,22 @@ TEST(BlockParserWithSchema, FailOnIncompleteJson) { ASSERT_RAISES(Invalid, ParseFromString(options, "{\"a\":0, \"b\"", &parsed)); } +TEST(BlockParserWithSchema, NullsInList) { + auto options = ParseOptions::Defaults(); + options.explicit_schema = schema({field("a", list(null()))}); + for (auto behavior : + {UnexpectedFieldBehavior::Error, UnexpectedFieldBehavior::Ignore}) { + options.unexpected_field_behavior = behavior; + AssertParseColumns(options, R"({"a": [null, null]})", {field("a", list(null()))}, + {"[[null, null]]"}); + } + + auto type = list(struct_({field("b", null())})); + options.explicit_schema = schema({field("a", type)}); + AssertParseColumns(options, R"({"a": [{"b": null}, null]})", {field("a", type)}, + {R"([[{"b": null}, null]])"}); +} + TEST(BlockParserWithSchema, ValidateIgnoredFields) { auto options = ParseOptions::Defaults(); options.explicit_schema = schema({field("known", int64())}); @@ -286,6 +302,17 @@ TEST(BlockParser, Null) { R"([{"plain": null}, {"plain": null}])"}); } +TEST(BlockParser, NullsInList) { + auto options = ParseOptions::Defaults(); + options.unexpected_field_behavior = UnexpectedFieldBehavior::InferType; + AssertParseColumns(options, R"({"a": [null, null], "b": [null, "hi", null]})", + {field("a", list(null())), field("b", list(utf8()))}, + {"[[null, null]]", R"([[null, "hi", null]])"}); + AssertParseColumns(options, R"({"a": [{"b": null}, null]})", + {field("a", list(struct_({field("b", null())})))}, + {R"([[{"b": null}, null]])"}); +} + TEST(BlockParser, InferNewFields) { std::string src = R"( {}