Repository: googleapis/python-bigquery

Issue:
some emoji will become wrong character stored in bigquery while using load_table_from_json
Thanks for stopping by to let us know something could be better!

**PLEASE READ**: If you have a support contract with Google, please create an issue in the [support console](https://cloud.google.com/support/) instead of filing on GitHub. This will ensure a timely response.

Please run down the following list and make sure you've tried the usual "quick fixes":

  - Search the issues already opened: https://github.com/googleapis/python-bigquery/issues
  - Search StackOverflow: https://stackoverflow.com/questions/tagged/google-cloud-platform+python

If you are still having issues, please be sure to include as much information as possible:

#### Environment details

  - OS type and version: macOS Big Sur
  - Python version: `python --version` 3.8.8
  - pip version: `pip --version`: 21.0.1
  - `google-cloud-bigquery` version: `pip show google-cloud-bigquery`: 2.6.1

#### Steps to reproduce

1. insert any emoji which is over `0xFFFF` into a `dict`, e.g. 🏦 (`\U0001F3E6`)
2. use `client.load_table_from_json(rows_to_insert...` to upload
3. see that emoji icon become �� in BigQuery web console

#### Code example

```python
    bank = "\U0001F3E6" 
    rows_to_insert = [{"emoji": bank}]
    client.load_table_from_json(rows_to_insert, table_id, job_config=job_config)
```

#### Stack trace
```
# example
```

Making sure to follow these steps will guarantee the quickest resolution possible.

Thanks!



Discussion:

Resolving patch:
diff --git a/google/cloud/bigquery/client.py b/google/cloud/bigquery/client.py
--- a/google/cloud/bigquery/client.py
+++ b/google/cloud/bigquery/client.py
@@ -2762,7 +2762,7 @@ def load_table_from_json(
 
         destination = _table_arg_to_table_ref(destination, default_project=self.project)
 
-        data_str = "\n".join(json.dumps(item) for item in json_rows)
+        data_str = "\n".join(json.dumps(item, ensure_ascii=False) for item in json_rows)
         encoded_str = data_str.encode()
         data_file = io.BytesIO(encoded_str)
         return self.load_table_from_file(


Test patch:
diff --git a/tests/unit/test_client.py b/tests/unit/test_client.py
--- a/tests/unit/test_client.py
+++ b/tests/unit/test_client.py
@@ -7775,6 +7775,42 @@ def test_load_table_from_json_w_invalid_job_config(self):
         err_msg = str(exc.value)
         assert "Expected an instance of LoadJobConfig" in err_msg
 
+    def test_load_table_from_json_unicode_emoji_data_case(self):
+        from google.cloud.bigquery.client import _DEFAULT_NUM_RETRIES
+
+        client = self._make_client()
+
+        emoji = "\U0001F3E6"
+        json_row = {"emoji": emoji}
+        json_rows = [json_row]
+
+        load_patch = mock.patch(
+            "google.cloud.bigquery.client.Client.load_table_from_file", autospec=True
+        )
+
+        with load_patch as load_table_from_file:
+            client.load_table_from_json(json_rows, self.TABLE_REF)
+
+        load_table_from_file.assert_called_once_with(
+            client,
+            mock.ANY,
+            self.TABLE_REF,
+            size=mock.ANY,
+            num_retries=_DEFAULT_NUM_RETRIES,
+            job_id=mock.ANY,
+            job_id_prefix=None,
+            location=client.location,
+            project=client.project,
+            job_config=mock.ANY,
+            timeout=None,
+        )
+
+        sent_data_file = load_table_from_file.mock_calls[0][1][1]
+
+        # make sure json_row's unicode characters are only encoded one time
+        expected_bytes = b'{"emoji": "' + emoji.encode("utf8") + b'"}'
+        assert sent_data_file.getvalue() == expected_bytes
+
     # Low-level tests
 
     @classmethod