Skip to content

Commit 00d4371

Browse files
committed
Fix nested JSON parameter normalization
1 parent 902efae commit 00d4371

2 files changed

Lines changed: 67 additions & 2 deletions

File tree

src_py/connection.py

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -188,8 +188,16 @@ def _contains_unresolved_json_type(cls, value: Any) -> bool:
188188
if value is None:
189189
return True
190190
if isinstance(value, (list, tuple)):
191-
return len(value) == 0 or any(
192-
cls._contains_unresolved_json_type(item) for item in value
191+
if len(value) == 0:
192+
return True
193+
has_nested_child = any(
194+
isinstance(item, (list, tuple, dict)) for item in value
195+
)
196+
has_scalar_child = any(
197+
not isinstance(item, (list, tuple, dict)) for item in value
198+
)
199+
return any(cls._contains_unresolved_json_type(item) for item in value) or (
200+
has_nested_child and has_scalar_child
193201
)
194202
if isinstance(value, dict):
195203
return len(value) == 0 or any(

test/test_json.py

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
from __future__ import annotations
22

3+
import builtins
34
import json
45
from typing import TYPE_CHECKING
56

@@ -84,6 +85,62 @@ def test_to_json_python_param_with_empty_nested_list(conn_db_empty: ConnDB) -> N
8485
assert response_data == data
8586

8687

88+
def test_to_json_python_param_with_mixed_nested_list(conn_db_empty: ConnDB) -> None:
89+
conn, _ = conn_db_empty
90+
conn.execute("""
91+
INSTALL json;
92+
LOAD json;
93+
CREATE NODE TABLE User (id SERIAL PRIMARY KEY, meta JSON);
94+
""")
95+
96+
data = {
97+
"@context": [
98+
"entry1",
99+
"entry2",
100+
{"key": "value"},
101+
],
102+
}
103+
104+
response = conn.execute(
105+
"""
106+
CREATE (n:User {meta: to_json($meta)})
107+
RETURN n.id as id, cast(n.meta AS STRING) as meta;
108+
""",
109+
parameters={"meta": data},
110+
)
111+
112+
response_data = json.loads(response.rows_as_dict().get_all()[0]["meta"])
113+
assert response_data == data
114+
115+
116+
def test_to_json_mixed_nested_list_normalization_does_not_import_numpy(
117+
conn_db_empty: ConnDB,
118+
monkeypatch,
119+
) -> None:
120+
conn, _ = conn_db_empty
121+
query = "CREATE (n:User {meta: to_json($meta)})"
122+
data = {"@context": ["entry1", "entry2", {"key": "value"}]}
123+
parameters = {"meta": data}
124+
125+
real_import = builtins.__import__
126+
127+
def guarded_import(name, *args, **kwargs):
128+
if name == "numpy" or name.startswith("numpy."):
129+
msg = "JSON parameter normalization should not import NumPy"
130+
raise AssertionError(msg)
131+
return real_import(name, *args, **kwargs)
132+
133+
monkeypatch.setattr(builtins, "__import__", guarded_import)
134+
135+
normalized_query, normalized_parameters = conn._normalize_parameters_for_pybind(
136+
query,
137+
parameters,
138+
)
139+
assert normalized_query.startswith("CREATE (n:User {meta: CAST(")
140+
assert normalized_query.endswith(" AS JSON)})")
141+
assert normalized_parameters == {}
142+
143+
87144
def test_to_json_python_param_with_homogeneous_list_uses_typed_binding(
88145
conn_db_empty: ConnDB,
89146
) -> None:

0 commit comments

Comments
 (0)