diff --git a/awx/api/parsers.py b/awx/api/parsers.py index 826c67189a..dbb659e7d9 100644 --- a/awx/api/parsers.py +++ b/awx/api/parsers.py @@ -12,6 +12,26 @@ from rest_framework import parsers from rest_framework.exceptions import ParseError +def _remove_trailing_commas(data): + left = 0 + right = 0 + in_string = False + ret = [] + while left != len(data): + if data[left] == ',' and not in_string: + while right != len(data) and data[right] in ',\n\t\r ': + right += 1 + if right == len(data) or data[right] not in '}]': + ret.append(',') + else: + if data[left] == '"' and (left - 1 >= 0 and data[left - 1] != '\\'): + in_string = not in_string + ret.append(data[left]) + right += 1 + left = right + return ''.join(ret) + + class JSONParser(parsers.JSONParser): """ Parses JSON-serialized data, preserving order of dictionary keys. @@ -25,7 +45,7 @@ class JSONParser(parsers.JSONParser): encoding = parser_context.get('encoding', settings.DEFAULT_CHARSET) try: - data = stream.read().decode(encoding) + data = _remove_trailing_commas(stream.read().decode(encoding)) obj = json.loads(data, object_pairs_hook=OrderedDict) if not isinstance(obj, dict): raise ParseError(_('JSON parse error - not a JSON object')) diff --git a/awx/main/tests/unit/api/test_parsers.py b/awx/main/tests/unit/api/test_parsers.py new file mode 100644 index 0000000000..eae4a14913 --- /dev/null +++ b/awx/main/tests/unit/api/test_parsers.py @@ -0,0 +1,15 @@ +import pytest + +from awx.api.parsers import _remove_trailing_commas + + +@pytest.mark.parametrize('input_, output', [ + ('{"foo": "bar"}', '{"foo": "bar"}'), + ('{"foo": "bar",\n\t\r }', '{"foo": "bar"}'), + ('{"foo": ["alice", "bob"]}', '{"foo": ["alice","bob"]}'), + ('{"foo": ["alice", "bob",\n\t\r ]}', '{"foo": ["alice","bob"]}'), + ('{"foo": "\\"bar,\n\t\r }"}', '{"foo": "\\"bar,\n\t\r }"}'), + ('{"foo": ["\\"alice,\n\t\r ]", "bob"]}', '{"foo": ["\\"alice,\n\t\r ]","bob"]}'), +]) +def test_remove_trailing_commas(input_, output): + assert _remove_trailing_commas(input_) == output