Skip to content

Commit

Permalink
fix(json): Fix keys not being escaped correctly in json.dumps
Browse files Browse the repository at this point in the history
Fixes #15
  • Loading branch information
LukeSavefrogs committed Jan 17, 2024
1 parent ff43cce commit 4243654
Show file tree
Hide file tree
Showing 2 changed files with 63 additions and 2 deletions.
28 changes: 26 additions & 2 deletions src/polyfills/json/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,20 @@

__all__ = ["dumps", "dump", "loads", "load"]

def escape_string(string):
# type: (str) -> str
"""Escapes a string so that it can be used as a JSON string.
Args:
string (str): The string to escape.
Returns:
str: The escaped string.
"""
return string \
.replace("\\", "\\\\") \
.replace('"', '\\"')

def dumps(
obj,
indent=None, # type: int|None
Expand Down Expand Up @@ -58,7 +72,17 @@ def dumps(
if len(obj_string_parts) > 1:
obj_string_parts[-1] += ", "
value = dumps(obj[key], indent_spaces, truthy_value, falsy_value)
obj_string_parts.append('"%s": %s' % (str(key), str(value)))
parsed_key = dumps(key)

# Remove quotes from the parsed key, because we will add them
# manually later.
if parsed_key.startswith('"') and parsed_key.endswith('"'):
parsed_key = parsed_key[1:-1]

obj_string_parts.append('"%s": %s' % (
parsed_key,
str(value),
))

obj_string_parts.append("}")
#endif
Expand Down Expand Up @@ -111,7 +135,7 @@ def dumps(

# ---> Base types
elif obj_type == type(""):
return '"%s"' % str(obj).replace("\\", "\\\\").replace('"', '\\"')
return '"%s"' % escape_string(str(obj))
elif obj_type == type(5):
return str(obj)
elif obj_type == type(5.0):
Expand Down
37 changes: 37 additions & 0 deletions src/polyfills/json/tests/dumps/test_dumps.py
Original file line number Diff line number Diff line change
Expand Up @@ -117,6 +117,43 @@ def test_simple(self):
'{"key": "value"}'
)

def test_conversion_keys(self):
self.assertEqual(
json.dumps({True: 1, False: 0, None: "null"}),
'{"true": 1, "false": 0, "null": "null"}'
)

def test_quotes(self):
""" Quotes should be escaped (see #15) """
self.assertEqual(
json.dumps({"\"": "\""}),
r'{"\"": "\""}',
"Double quotes should be escaped both in keys and values"
)
self.assertEqual(
json.dumps({"'": "'"}),
r"""{"'": "'"}""",
"Single quotes should be kept as-is",
)


def test_escape(self):
self.assertEqual(
json.dumps({"key": "va'lue"}),
r"""{"key": "va'lue"}""",
"Single quotes do not need to be escaped"
)
self.assertEqual(
json.dumps({'k"ey': 'va"lue'}),
r"""{"k\"ey": "va\"lue"}""",
"Double quotes should be escaped"
)
self.assertEqual(
json.dumps({r"k\ey": r"va\lue"}),
r"""{"k\\ey": "va\\lue"}""",
"Backslashes should be escaped both in keys and values"
)

def test_complex(self):
if str(1==1) == 'True':
__true__ = 1==1
Expand Down

0 comments on commit 4243654

Please sign in to comment.