diff --git a/src/jsonata/jsonata.py b/src/jsonata/jsonata.py index ed99a66..d3303c3 100644 --- a/src/jsonata/jsonata.py +++ b/src/jsonata/jsonata.py @@ -512,15 +512,21 @@ def evaluate_filter(self, predicate: Optional[Any], input: Optional[Any], enviro results = utils.Utils.create_sequence() if isinstance(input, utils.Utils.JList) and input.tuple_stream: results.tuple_stream = True - if not (isinstance(input, list)): + if input is None: + # undefined input yields undefined output; skip filtering entirely + input = utils.Utils.create_sequence() + elif not (isinstance(input, list)): input = utils.Utils.create_sequence(input) if predicate.type == "number": index = int(predicate.value) # round it down - was Math.floor if index < 0: # count in from end of array index = len(input) + index - item = input[index] if 0 <= index < len(input) else None - if item is not None: + if 0 <= index < len(input): + item = input[index] + # Preserve JSON null at this index (vs. out-of-bounds, which is undefined) + if item is None: + item = utils.Utils.NULL_VALUE if isinstance(item, list): results = item else: diff --git a/tests/null_safety_test.py b/tests/null_safety_test.py index 4b1543f..49e95a1 100644 --- a/tests/null_safety_test.py +++ b/tests/null_safety_test.py @@ -31,3 +31,10 @@ def test_null_safety(self): res = jsonata.Jsonata("$spread(null)").evaluate(None) assert res is None + + def test_array_index_preserves_null(self): + # Indexing into an array element that is JSON null must yield null, + # not be filtered out as if it were undefined. + data = {"data": [[1, None, 3], [2, None, 4], [3, None, 5]]} + res = jsonata.Jsonata("[$map(data, function($row) { $row[1] })]").evaluate(data) + assert res == [None, None, None]