@@ -155,3 +155,83 @@ def test_to_json_python_param_with_homogeneous_list_uses_typed_binding(
155155
156156 assert normalized_query == query
157157 assert normalized_parameters == parameters
158+
159+
160+ def test_get_as_df_json_scalar (conn_db_empty : ConnDB ) -> None :
161+ """Scalar JSON values convert through get_as_df() as Python strings.
162+
163+ Covers non-null values, typed JSON nulls, and a mixed column in a single query.
164+ """
165+ conn , _ = conn_db_empty
166+ df = conn .execute (
167+ "UNWIND ["
168+ 'CAST(\' {"a": 1, "b": [2, 3]}\' AS JSON), '
169+ "CAST(NULL AS JSON), "
170+ "CAST('[1, 2, 3]' AS JSON)"
171+ "] AS j RETURN j"
172+ ).get_as_df ()
173+
174+ assert str (df ["j" ].dtype ) == "object"
175+ assert df ["j" ].isna ().tolist () == [False , True , False ]
176+
177+ first = df ["j" ].iloc [0 ]
178+ assert isinstance (first , str )
179+ assert json .loads (first ) == {"a" : 1 , "b" : [2 , 3 ]}
180+
181+ third = df ["j" ].iloc [2 ]
182+ assert isinstance (third , str )
183+ assert json .loads (third ) == [1 , 2 , 3 ]
184+
185+
186+ def test_get_as_df_json_empty_result (conn_db_empty : ConnDB ) -> None :
187+ """An empty result over a JSON column builds the column without crashing.
188+
189+ convertToArrayType() runs during NPArrayWrapper construction, before any
190+ rows are iterated, so a zero-row JSON result is the minimal reproduction
191+ for the original dtype-selection crash.
192+ """
193+ conn , _ = conn_db_empty
194+ conn .execute ("CREATE NODE TABLE t (id SERIAL PRIMARY KEY, data JSON)" )
195+
196+ df = conn .execute ("MATCH (n:t) RETURN n.data AS data" ).get_as_df ()
197+
198+ assert len (df ) == 0
199+ assert str (df ["data" ].dtype ) == "object"
200+
201+
202+ def test_get_as_df_json_extract (conn_db_empty : ConnDB ) -> None :
203+ """json_extract() produces a scalar JSON result that converts via get_as_df(). """
204+ conn , _ = conn_db_empty
205+ conn .execute ("INSTALL json; LOAD json;" )
206+ conn .execute ("CREATE NODE TABLE t (id SERIAL PRIMARY KEY, data JSON)" )
207+
208+ data = {"name" : {"first" : "Alice" , "last" : "Smith" }}
209+ conn .execute (
210+ "CREATE (n:t {data: to_json($data)})" ,
211+ parameters = {"data" : json .dumps (data )},
212+ )
213+
214+ df = conn .execute ("MATCH (n:t) RETURN json_extract(n.data, '$.name') AS name" ).get_as_df ()
215+
216+ assert str (df ["name" ].dtype ) == "object"
217+ val = df ["name" ].iloc [0 ]
218+ assert isinstance (val , str )
219+ assert json .loads (val ) == {"first" : "Alice" , "last" : "Smith" }
220+
221+
222+ def test_get_as_df_json_list (conn_db_empty : ConnDB ) -> None :
223+ """ JSON[] (LIST of JSON) columns keep their existing pandas behavior. """
224+ conn , _ = conn_db_empty
225+ conn .execute ("INSTALL json; LOAD json;" )
226+ conn .execute ("CREATE NODE TABLE t (id SERIAL PRIMARY KEY, data JSON[])" )
227+
228+ data = [{"x" : 1 }, {"x" : 2 }, {"x" : 3 }]
229+ conn .execute ("CREATE (n:t {data: $d})" , parameters = {"d" : data })
230+
231+ df = conn .execute ("MATCH (n:t) RETURN n.data AS data" ).get_as_df ()
232+
233+ assert str (df ["data" ].dtype ) == "object"
234+ val = df ["data" ].iloc [0 ]
235+ assert isinstance (val , list )
236+ assert len (val ) == 3
237+ assert all (isinstance (e , str ) for e in val )
0 commit comments