Skip to content

Commit

Permalink
Serialize floats in JSON as strings so they aren't converted to integ…
Browse files Browse the repository at this point in the history
…ers in JavaScript.
  • Loading branch information
adamghill committed Dec 5, 2020
1 parent aaf62e8 commit 0e437c9
Show file tree
Hide file tree
Showing 2 changed files with 42 additions and 3 deletions.
3 changes: 2 additions & 1 deletion django_unicorn/components.py
Original file line number Diff line number Diff line change
Expand Up @@ -104,7 +104,8 @@ def render(self):

content = response.content.decode("utf-8")

checksum = generate_checksum(str.encode(str(self.frontend_context_variables)))
frontend_context_variables_dict = orjson.loads(self.frontend_context_variables)
checksum = generate_checksum(orjson.dumps(frontend_context_variables_dict))

soup = BeautifulSoup(content, features="html.parser")
root_element = UnicornTemplateResponse._get_root_element(soup)
Expand Down
42 changes: 40 additions & 2 deletions django_unicorn/serializer.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
from typing import Any
from typing import Any, Dict, List

from django.core.serializers import serialize
from django.db.models import Model, QuerySet
Expand Down Expand Up @@ -48,13 +48,51 @@ def _json_serializer(obj):
raise TypeError


def _fix_floats(current: Dict, data: Dict = None, paths: List = []) -> None:
"""
Recursively change any Python floats to string so that JavaScript
won't change float to integers.
Params:
current: Dictionary in which to check for and fix floats.
"""

if data is None:
data = current

if isinstance(current, dict):
for key, val in current.items():
paths.append(key)
_fix_floats(val, data, paths=paths)
paths.pop()
elif isinstance(current, list):
for (idx, item) in enumerate(current):
paths.append(idx)
_fix_floats(item, data, paths=paths)
paths.pop()
elif isinstance(current, float):
_piece = data

for (idx, path) in enumerate(paths):
if idx == len(paths) - 1:
# `path` can be a dictionary key or list index,
# but either way it is retrieved the same way
_piece[path] = str(current)
else:
_piece = _piece[path]


def dumps(data: dict) -> str:
"""
Converts the passed-in dictionary to a string representation.
Handles the following objects: dataclass, datetime, enum, float, int, numpy, str, uuid,
Django Model, Django QuerySet, any object with `to_json` method.
"""
dumped_data = orjson.dumps(data, default=_json_serializer).decode("utf-8")
serialized_data = orjson.dumps(data, default=_json_serializer)
dict_data = orjson.loads(serialized_data)
_fix_floats(dict_data)

dumped_data = orjson.dumps(dict_data).decode("utf-8")

return dumped_data

0 comments on commit 0e437c9

Please sign in to comment.