Skip to content

Commit 172fa11

Browse files
committed
Convert non-json-serializable properties to strings
1 parent d3ded3d commit 172fa11

File tree

3 files changed

+48
-9
lines changed

3 files changed

+48
-9
lines changed

changelog.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
## Improvements
1414

1515
* Allow for `Node` and `Relationship` inputs to be given as `camelCase` and `SCREAMING_SNAKE_CASE` (in addition to `snake_case`).
16+
* Convert non-json serializable properties to strings in the `render` method, instead of raising an error.
1617

1718

1819
## Other changes

python-wrapper/src/neo4j_viz/nvl.py

Lines changed: 30 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
import json
44
import uuid
55
from importlib.resources import files
6+
from typing import Any, Union
67

78
from IPython.display import HTML
89

@@ -40,11 +41,37 @@ def __init__(self) -> None:
4041
with screenshot_path.open("r", encoding="utf-8") as file:
4142
self.screenshot_svg = file.read()
4243

43-
def unsupported_field_type_error(self, e: TypeError, entity: str) -> Exception:
44+
@staticmethod
45+
def unsupported_field_type_error(e: TypeError, entity: str) -> Exception:
4446
if "not JSON serializable" in str(e):
4547
return ValueError(f"A field of a {entity} object is not supported: {str(e)}")
4648
return e
4749

50+
@staticmethod
51+
def _serialize_entity(entity: Union[Node, Relationship]) -> dict[str, Any]:
52+
try:
53+
entity_dict = entity.to_dict()
54+
json.dumps(entity_dict)
55+
return entity_dict
56+
except TypeError:
57+
props_as_strings = {}
58+
for k, v in entity_dict["properties"].items():
59+
try:
60+
json.dumps(v)
61+
except TypeError:
62+
props_as_strings[k] = str(v)
63+
entity_dict["properties"].update(props_as_strings)
64+
65+
try:
66+
json.dumps(entity_dict)
67+
return entity_dict
68+
except TypeError as e:
69+
# This should never happen anymore, but just in case
70+
if "not JSON serializable" in str(e):
71+
raise ValueError(f"A field of a {type(entity).__name__} object is not supported: {str(e)}")
72+
else:
73+
raise e
74+
4875
def render(
4976
self,
5077
nodes: list[Node],
@@ -54,14 +81,8 @@ def render(
5481
height: str,
5582
show_hover_tooltip: bool,
5683
) -> HTML:
57-
try:
58-
nodes_json = json.dumps([node.to_dict() for node in nodes])
59-
except TypeError as e:
60-
raise self.unsupported_field_type_error(e, "node")
61-
try:
62-
rels_json = json.dumps([rel.to_dict() for rel in relationships])
63-
except TypeError as e:
64-
raise self.unsupported_field_type_error(e, "relationship")
84+
nodes_json = json.dumps([self._serialize_entity(node) for node in nodes])
85+
rels_json = json.dumps([self._serialize_entity(rel) for rel in relationships])
6586

6687
render_options_json = json.dumps(render_options.to_dict())
6788
container_id = str(uuid.uuid4())

python-wrapper/tests/test_render.py

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -121,3 +121,20 @@ def test_render_warnings() -> None:
121121
"relationships. If you need these features, use the canvas renderer by setting the `renderer` parameter",
122122
):
123123
VG.render(max_allowed_nodes=20_000, renderer=Renderer.WEB_GL)
124+
125+
126+
def test_render_non_json_serializable() -> None:
127+
import datetime
128+
129+
nodes = [
130+
Node(
131+
id=0,
132+
properties={
133+
"non-json-serializable": datetime.datetime.now(),
134+
},
135+
)
136+
]
137+
138+
VG = VisualizationGraph(nodes=nodes, relationships=[])
139+
140+
VG.render()

0 commit comments

Comments
 (0)