Repository: googleapis/python-bigquery

Issue:
Easy way to unmarshall/dump objects to Json?
Sorry if I missed it, but could not find it in the documentation or after playing around. 

Basically I am trying to get the raw json dump of objects like Tables or [TableList](https://cloud.google.com/bigquery/docs/reference/rest/v2/tables/list#response-body), but via the API we are getting many composed objects that need to be unmarshalled manually one by one.

For example: what would be a way to dump into json a TableList? Thanks!

```
from google.cloud import bigquery
import json

client = bigquery.Client()                
                
for d in client.list_datasets():
  for t in client.list_tables(d.reference):
    print(t)
    print(repr(t))
    print(dir(t))
    print(t.table_id)
    print(json.dumps(t))
```
    
    
```
<google.cloud.bigquery.table.TableListItem object at 0x7f02c30b0760>
<google.cloud.bigquery.table.TableListItem object at 0x7f02c30b0760>
['__class__', '__delattr__', '__dict__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__gt__', '__hash__', '__init__', '__init_subclass__', '__le__', '__lt__', '__module__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', '__weakref__', '_properties', 'clustering_fields', 'created', 'dataset_id', 'expires', 'friendly_name', 'from_string', 'full_table_id', 'labels', 'partition_expiration', 'partitioning_type', 'project', 'reference', 'table_id', 'table_type', 'time_partitioning', 'to_bqstorage', 'view_use_legacy_sql']
arr
Traceback (most recent call last):
  File "<stdin>", line 7, in <module>
  File "/usr/lib/python3.8/json/__init__.py", line 231, in dumps
    return _default_encoder.encode(obj)
  File "/usr/lib/python3.8/json/encoder.py", line 199, in encode
    chunks = self.iterencode(o, _one_shot=True)
  File "/usr/lib/python3.8/json/encoder.py", line 257, in iterencode
    return _iterencode(o, 0)
  File "/usr/lib/python3.8/json/encoder.py", line 179, in default
    raise TypeError(f'Object of type {o.__class__.__name__} '
TypeError: Object of type TableListItem is not JSON serializable
    
```
    
-->

`{"created":"2020-10-05T01:43:40.077Z","dataset_id":"d","description":null, ...}`



Discussion:
2 options for you:

(1) Most objects have a `to_api_repr` method, which produces a JSON-serilizable value for the class. https://googleapis.dev/python/bigquery/latest/generated/google.cloud.bigquery.job.QueryJobConfig.html#google.cloud.bigquery.job.QueryJobConfig.to_api_repr

(2) It's not a public method, but most objects in this library have a hidden `_properties` property, which is JSON-serializable. You can try access that.
Looks like TableListItem does not have a `to_api_repr` method. I'll leave this open as a feature request to add that method there.

https://googleapis.dev/python/bigquery/latest/generated/google.cloud.bigquery.table.TableListItem.html#google.cloud.bigquery.table.TableListItem
I see, this is helpful, thanks a lot! I will use `_properties` in the meantime.

Resolving patch:
diff --git a/google/cloud/bigquery/table.py b/google/cloud/bigquery/table.py
--- a/google/cloud/bigquery/table.py
+++ b/google/cloud/bigquery/table.py
@@ -1071,6 +1071,14 @@ def to_bqstorage(self):
         """
         return self.reference.to_bqstorage()
 
+    def to_api_repr(self):
+        """Constructs the API resource of this table
+
+        Returns:
+            Dict[str, object]: Table represented as an API resource
+        """
+        return copy.deepcopy(self._properties)
+
 
 def _row_from_mapping(mapping, schema):
     """Convert a mapping to a row tuple using the schema.


Test patch:
diff --git a/tests/unit/test_table.py b/tests/unit/test_table.py
--- a/tests/unit/test_table.py
+++ b/tests/unit/test_table.py
@@ -1433,6 +1433,17 @@ def test_labels_update_in_place(self):
         labels["foo"] = "bar"  # update in place
         self.assertEqual(table.labels, {"foo": "bar"})
 
+    def test_to_api_repr(self):
+        resource = {
+            "tableReference": {
+                "projectId": "testproject",
+                "datasetId": "testdataset",
+                "tableId": "testtable",
+            }
+        }
+        table = self._make_one(resource)
+        self.assertEqual(table.to_api_repr(), resource)
+
 
 class TestRow(unittest.TestCase):
     def test_row(self):