Skip to content

Commit 8c0427a

Browse files
authored
Add support for None values in list parsing (boto#3618)
* Fix null value handling in list parsing for all protocols When services return lists containing null values (e.g., RDS Data API returning arrays with nulls), the parser would fail with TypeError when attempting to parse primitive types like integers. The fix adds explicit null handling in _handle_list() to preserve null values in the parsed output. Fixes boto/boto3#4508
1 parent 374c632 commit 8c0427a

2 files changed

Lines changed: 59 additions & 1 deletion

File tree

botocore/parsers.py

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -352,7 +352,12 @@ def _handle_list(self, shape, node):
352352
parsed = []
353353
member_shape = shape.member
354354
for item in node:
355-
parsed.append(self._parse_shape(member_shape, item))
355+
# Treat all lists as sparse during parsing to safely handle null
356+
# elements that may be present in service responses.
357+
if item is None:
358+
parsed.append(None)
359+
else:
360+
parsed.append(self._parse_shape(member_shape, item))
356361
return parsed
357362

358363
def _default_handle(self, shape, value):

tests/unit/test_parsers.py

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1646,3 +1646,56 @@ def test_can_handle_generic_error_message(parser, body):
16461646
)
16471647
assert parsed['Error'] == {'Code': '503', 'Message': 'Service Unavailable'}
16481648
assert parsed['ResponseMetadata']['HTTPStatusCode'] == 503
1649+
1650+
1651+
class TestNullInListParsing(unittest.TestCase):
1652+
def test_handles_null_in_list(self):
1653+
parser = parsers.JSONParser()
1654+
body = b'{"items":[1,null,3]}'
1655+
output_shape = model.StructureShape(
1656+
'OutputShape',
1657+
{'type': 'structure', 'members': {'items': {'shape': 'ItemList'}}},
1658+
model.ShapeResolver(
1659+
{
1660+
'ItemList': {
1661+
'type': 'list',
1662+
'member': {'shape': 'ItemType'},
1663+
},
1664+
'ItemType': {'type': 'integer'},
1665+
}
1666+
),
1667+
)
1668+
parsed = parser.parse(
1669+
{'body': body, 'headers': {}, 'status_code': 200}, output_shape
1670+
)
1671+
self.assertEqual(parsed['items'], [1, None, 3])
1672+
1673+
def test_handles_null_in_nested_list(self):
1674+
parser = parsers.RestJSONParser()
1675+
body = b'{"data":[[{"values":[1,null,3]}]]}'
1676+
output_shape = model.StructureShape(
1677+
'OutputShape',
1678+
{'type': 'structure', 'members': {'data': {'shape': 'DataList'}}},
1679+
model.ShapeResolver(
1680+
{
1681+
'DataList': {
1682+
'type': 'list',
1683+
'member': {'shape': 'ItemList'},
1684+
},
1685+
'ItemList': {'type': 'list', 'member': {'shape': 'Item'}},
1686+
'Item': {
1687+
'type': 'structure',
1688+
'members': {'values': {'shape': 'ValueList'}},
1689+
},
1690+
'ValueList': {
1691+
'type': 'list',
1692+
'member': {'shape': 'ValueType'},
1693+
},
1694+
'ValueType': {'type': 'integer'},
1695+
}
1696+
),
1697+
)
1698+
parsed = parser.parse(
1699+
{'body': body, 'headers': {}, 'status_code': 200}, output_shape
1700+
)
1701+
self.assertEqual(parsed['data'][0][0]['values'], [1, None, 3])

0 commit comments

Comments
 (0)