Skip to content

Commit 92a3169

Browse files
authored
feat: support JSON object consisting of an array. (#782)
1 parent 05f2a24 commit 92a3169

File tree

3 files changed

+54
-5
lines changed

3 files changed

+54
-5
lines changed

google/cloud/spanner_v1/data_types.py

Lines changed: 20 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -19,16 +19,30 @@
1919

2020
class JsonObject(dict):
2121
"""
22-
JsonObject type help format Django JSONField to compatible Cloud Spanner's
23-
JSON type. Before making queries, it'll help differentiate between
24-
normal parameters and JSON parameters.
22+
Provides functionality of JSON data type in Cloud Spanner
23+
API, mimicking simple `dict()` behaviour and making
24+
all the necessary conversions under the hood.
2525
"""
2626

2727
def __init__(self, *args, **kwargs):
2828
self._is_null = (args, kwargs) == ((), {}) or args == (None,)
29+
self._is_array = len(args) and isinstance(args[0], (list, tuple))
30+
31+
# if the JSON object is represented with an array,
32+
# the value is contained separately
33+
if self._is_array:
34+
self._array_value = args[0]
35+
return
36+
2937
if not self._is_null:
3038
super(JsonObject, self).__init__(*args, **kwargs)
3139

40+
def __repr__(self):
41+
if self._is_array:
42+
return str(self._array_value)
43+
44+
return super(JsonObject, self).__repr__()
45+
3246
@classmethod
3347
def from_str(cls, str_repr):
3448
"""Initiate an object from its `str` representation.
@@ -53,4 +67,7 @@ def serialize(self):
5367
if self._is_null:
5468
return None
5569

70+
if self._is_array:
71+
return json.dumps(self._array_value, sort_keys=True, separators=(",", ":"))
72+
5673
return json.dumps(self, sort_keys=True, separators=(",", ":"))

tests/system/test_dbapi.py

Lines changed: 33 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -339,8 +339,10 @@ def test_DDL_autocommit(shared_instance, dbapi_database):
339339

340340
@pytest.mark.skipif(_helpers.USE_EMULATOR, reason="Emulator does not support json.")
341341
def test_autocommit_with_json_data(shared_instance, dbapi_database):
342-
"""Check that DDLs in autocommit mode are immediately executed for
343-
json fields."""
342+
"""
343+
Check that DDLs in autocommit mode are immediately
344+
executed for json fields.
345+
"""
344346
# Create table
345347
conn = Connection(shared_instance, dbapi_database)
346348
conn.autocommit = True
@@ -376,6 +378,35 @@ def test_autocommit_with_json_data(shared_instance, dbapi_database):
376378
conn.close()
377379

378380

381+
@pytest.mark.skipif(_helpers.USE_EMULATOR, reason="Emulator does not support json.")
382+
def test_json_array(shared_instance, dbapi_database):
383+
# Create table
384+
conn = Connection(shared_instance, dbapi_database)
385+
conn.autocommit = True
386+
387+
cur = conn.cursor()
388+
cur.execute(
389+
"""
390+
CREATE TABLE JsonDetails (
391+
DataId INT64 NOT NULL,
392+
Details JSON,
393+
) PRIMARY KEY (DataId)
394+
"""
395+
)
396+
cur.execute(
397+
"INSERT INTO JsonDetails (DataId, Details) VALUES (%s, %s)",
398+
[1, JsonObject([1, 2, 3])],
399+
)
400+
401+
cur.execute("SELECT * FROM JsonDetails WHERE DataId = 1")
402+
row = cur.fetchone()
403+
assert isinstance(row[1], JsonObject)
404+
assert row[1].serialize() == "[1,2,3]"
405+
406+
cur.execute("DROP TABLE JsonDetails")
407+
conn.close()
408+
409+
379410
def test_DDL_commit(shared_instance, dbapi_database):
380411
"""Check that DDLs in commit mode are executed on calling `commit()`."""
381412
conn = Connection(shared_instance, dbapi_database)

tests/system/test_session_api.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -120,6 +120,7 @@
120120
AllTypesRowData(pkey=108, timestamp_value=NANO_TIME),
121121
AllTypesRowData(pkey=109, numeric_value=NUMERIC_1),
122122
AllTypesRowData(pkey=110, json_value=JSON_1),
123+
AllTypesRowData(pkey=111, json_value=[JSON_1, JSON_2]),
123124
# empty array values
124125
AllTypesRowData(pkey=201, int_array=[]),
125126
AllTypesRowData(pkey=202, bool_array=[]),

0 commit comments

Comments
 (0)