From becbd14892e3cbb365358a156da928abba05921a Mon Sep 17 00:00:00 2001 From: Robert Yokota Date: Sun, 24 May 2026 11:46:50 -0700 Subject: [PATCH] Preserve JSON null when indexing into an array --- src/jsonata/Jsonata.cpp | 23 +++++++++++++++-------- test/NullSafetyTest.cpp | 10 ++++++++++ 2 files changed, 25 insertions(+), 8 deletions(-) diff --git a/src/jsonata/Jsonata.cpp b/src/jsonata/Jsonata.cpp index 8dc42c1..fae86a0 100644 --- a/src/jsonata/Jsonata.cpp +++ b/src/jsonata/Jsonata.cpp @@ -1868,6 +1868,9 @@ std::any Jsonata::evaluateFilter(std::shared_ptr predicate, if (inputSequence.tupleStream) { results.tupleStream = true; } + } else if (!input.has_value()) { + // undefined input yields undefined output; skip filtering entirely + inputSequence = Utils::createSequence(); } else if (!Utils::isArray(input)) { inputSequence = Utils::createSequence(input); } else { @@ -1893,14 +1896,18 @@ std::any Jsonata::evaluateFilter(std::shared_ptr predicate, // ((List)input).get(index) : null; if (index >= 0 && index < static_cast(inputSequence.size())) { auto item = inputSequence[index]; - // Follow Java exactly: only add if item != null - if (item.has_value()) { - // Java reference line 505: if(item instanceof List) - if (Utils::isArray(item)) { - results = Utils::arrayify(item); - } else { - results.push_back(item); - } + // Preserve JSON null at this index (vs. out-of-bounds, which is + // undefined). An empty std::any here means the element was a + // JSON null; promote it to NULL_VALUE so downstream + // has_value() filtering keeps it. + if (!item.has_value()) { + item = Utils::NULL_VALUE; + } + // Java reference line 505: if(item instanceof List) + if (Utils::isArray(item)) { + results = Utils::arrayify(item); + } else { + results.push_back(item); } } } catch (const std::bad_any_cast&) { diff --git a/test/NullSafetyTest.cpp b/test/NullSafetyTest.cpp index a6e0b1c..04aeb81 100644 --- a/test/NullSafetyTest.cpp +++ b/test/NullSafetyTest.cpp @@ -69,6 +69,16 @@ TEST_F(NullSafetyTest, testSingleNull) { EXPECT_EQ(result.get(), 1); } +TEST_F(NullSafetyTest, testArrayIndexPreservesNull) { + // Indexing into an array element that is JSON null must yield null, + // not be filtered out as if it were undefined. + auto data = nlohmann::ordered_json::parse( + R"({"data": [[1, null, 3], [2, null, 4], [3, null, 5]]})"); + auto result = Jsonata("[$map(data, function($row) { $row[1] })]").evaluate(data); + auto expected = nlohmann::ordered_json::parse("[null, null, null]"); + EXPECT_EQ(result, expected); +} + TEST_F(NullSafetyTest, testFilterNullLookup) { nlohmann::ordered_json arrayData = nlohmann::ordered_json::array({ nlohmann::ordered_json::object({{"content", "some"}}),