diff --git a/google/cloud/bigquery/_helpers.py b/google/cloud/bigquery/_helpers.py index 684cbfc12..13baea4ad 100644 --- a/google/cloud/bigquery/_helpers.py +++ b/google/cloud/bigquery/_helpers.py @@ -17,6 +17,7 @@ import base64 import datetime import decimal +import json import math import re import os @@ -412,6 +413,12 @@ def _time_to_json(value): return value +def _json_from_json(value, field): + """Coerce 'value' to a pythonic JSON representation, if set or not nullable.""" + if _not_null(value, field): + return json.loads(value) + + # Converters used for scalar values marshalled as row data. _SCALAR_VALUE_TO_JSON_ROW = { "INTEGER": _int_to_json, @@ -427,6 +434,7 @@ def _time_to_json(value): "DATETIME": _datetime_to_json, "DATE": _date_to_json, "TIME": _time_to_json, + "JSON": _json_from_json, # Make sure DECIMAL and BIGDECIMAL are handled, even though # requests for them should be converted to NUMERIC. Better safe # than sorry. diff --git a/google/cloud/bigquery/query.py b/google/cloud/bigquery/query.py index 43591c648..a06ece503 100644 --- a/google/cloud/bigquery/query.py +++ b/google/cloud/bigquery/query.py @@ -469,7 +469,7 @@ def to_api_repr(self) -> dict: value = self.value converter = _SCALAR_VALUE_TO_JSON_PARAM.get(self.type_) if converter is not None: - value = converter(value) + value = converter(value) # type: ignore resource: Dict[str, Any] = { "parameterType": {"type": self.type_}, "parameterValue": {"value": value}, @@ -626,7 +626,7 @@ def to_api_repr(self) -> dict: converter = _SCALAR_VALUE_TO_JSON_PARAM.get(a_type["type"]) if converter is not None: - values = [converter(value) for value in values] + values = [converter(value) for value in values] # type: ignore a_values = [{"value": value} for value in values] resource = { @@ -775,7 +775,7 @@ def to_api_repr(self) -> dict: s_types[name] = {"name": name, "type": {"type": type_}} converter = _SCALAR_VALUE_TO_JSON_PARAM.get(type_) if converter is not None: - value = converter(value) + value = converter(value) # type: ignore values[name] = {"value": value} resource = { diff --git a/tests/unit/test__helpers.py b/tests/unit/test__helpers.py index e2e2da3c8..3c425da5f 100644 --- a/tests/unit/test__helpers.py +++ b/tests/unit/test__helpers.py @@ -58,6 +58,24 @@ def test_w_float_value(self): self.assertEqual(coerced, 42) +class Test_json_from_json(unittest.TestCase): + def _call_fut(self, value, field): + from google.cloud.bigquery._helpers import _json_from_json + + return _json_from_json(value, field) + + def test_w_none_nullable(self): + self.assertIsNone(self._call_fut(None, _Field("NULLABLE"))) + + def test_w_none_required(self): + with self.assertRaises(TypeError): + self._call_fut(None, _Field("REQUIRED")) + + def test_w_string_value(self): + coerced = self._call_fut('{"foo": true}', object()) + self.assertEqual(coerced, {"foo": True}) + + class Test_float_from_json(unittest.TestCase): def _call_fut(self, value, field): from google.cloud.bigquery._helpers import _float_from_json