From 9968ea0bbf8408c4a99aafa10f56748b80854b2f Mon Sep 17 00:00:00 2001 From: Cedric Conday Date: Tue, 30 Jun 2026 03:02:35 +0000 Subject: [PATCH] Deserialize a null list field as None instead of raising (#131) The Xero API can return null for an array field (e.g. reject_leave_application responds with "LeaveApplications": None). deserialize_list then tried to iterate over None and raised TypeError: 'NoneType' object is not iterable. Return None when data is None, mirroring how deserialize_model already handles a null payload. Closes #131. --- tests/test_api_client/test_deserializer.py | 7 +++++++ xero_python/api_client/deserializer.py | 2 ++ 2 files changed, 9 insertions(+) diff --git a/tests/test_api_client/test_deserializer.py b/tests/test_api_client/test_deserializer.py index 9c851a8a..731965bb 100644 --- a/tests/test_api_client/test_deserializer.py +++ b/tests/test_api_client/test_deserializer.py @@ -111,6 +111,13 @@ def test_deserialize_list_key_error(): deserialize_list(data_type, data, model_finder) +def test_deserialize_list_none(): + # regression for #131: the Xero API can return null for a list field + # (e.g. "LeaveApplications": None); deserializing must not raise. + result = deserialize_list("list[number]", None, model_finder=None) + assert result is None + + # deserialize_int tests @pytest.mark.parametrize("data,expected", [(n, int(n)) for n in ("1", "-1", 2)]) def test_deserialize_int(data, expected): diff --git a/xero_python/api_client/deserializer.py b/xero_python/api_client/deserializer.py index b64e9820..01af91d3 100644 --- a/xero_python/api_client/deserializer.py +++ b/xero_python/api_client/deserializer.py @@ -70,6 +70,8 @@ def deserialize_list(data_type, data, model_finder): :return: deserialized list """ + if data is None: + return None try: sub_data_type = LIST_DATA_TYPE.match(data_type).group(1) except AttributeError: