

本文為英文版的機器翻譯版本，如內容有任何歧義或不一致之處，概以英文版為準。

# 以 JSON 格式處理 Amazon RDS 資料 API 查詢結果
<a name="data-api-json"></a>

 當您呼叫 `ExecuteStatement` 操作，可以選擇以 JSON 格式的字串形式傳回查詢結果。這樣，您就可以使用程式設計語言的 JSON 剖析功能來解讀和重新格式化結果集。這樣做有助於避免編寫額外的程式碼來對結果集執行迴圈並解讀每個欄值。

 若要以 JSON 格式請求結果集，請傳遞選用 `formatRecordsAs` 參數並搭配 `JSON` 值。JSON 格式的結果集會在 `ExecuteStatementResponse` 結構的 `formattedRecords` 欄位中傳回。

 `BatchExecuteStatement` 動作不會傳回結果集。因此，JSON 選項不適用於該動作。

 若要自訂 JSON 雜湊結構中的鍵，請在結果集中定義欄別名。若要這麼做，請在 SQL 查詢的欄清單中使用 `AS` 子句。

 您可以使用 JSON 功能讓結果集更易於閱讀，並將其內容映射到語言特定的架構。由於 ASCII 編碼的結果集的量大於預設表示形式，您可以為傳回大量列或大型欄值的查詢 (這些查詢消耗的記憶體超過應用程式可用的記憶體) 選擇預設表示法。

**Topics**
+ [以 JSON 格式擷取查詢結果](#data-api-json-querying)
+ [資料類型映射](#data-api-json-datatypes)
+ [故障診斷](#data-api-json-troubleshooting)
+ [範例](#data-api-json-examples)

## 以 JSON 格式擷取查詢結果
<a name="data-api-json-querying"></a>

 若要以 JSON 字串形式接收結果集，請在 `ExecuteStatement` 呼叫中加入 `.withFormatRecordsAs(RecordsFormatType.JSON)`。傳回值會以 `formattedRecords` 欄位中的 JSON 字串傳回。在本案例中，`columnMetadata` 為 `null`。欄標籤是表示每列物件的鍵。結果集中的每一列都會重複這些欄名。欄值為帶引號的字串、數字值或表示`true`、`false`, 或 `null` 的特殊值。欄中繼資料 (如長度限制以及數字和字串的精確類型) 不會保留在 JSON 回應中。

 如果省略 `.withFormatRecordsAs()` 呼叫或指定 `NONE` 的參數，則結果集將使用 `Records` 和 `columnMetadata` 欄位以二進位格式傳回。

## 資料類型映射
<a name="data-api-json-datatypes"></a>

 結果集中的 SQL 值映射至一組較小的 JSON 類型。這些值在 JSON 中表示為字串、數字和一些特殊的常數，例如 `true`、`false`，和 `null`。您可以將這些值轉換為應用程式中的變數，根據您的程式設計語言使用強式或弱式輸入。


****  

|   JDBC 資料類型   |   JSON 資料類型   | 
| --- | --- | 
|   `INTEGER`, `TINYINT`, `SMALLINT`, `BIGINT`   |   預設為數字。如果 `LongReturnType` 選項設定為 `STRING` 則為字串。  | 
|   `FLOAT`, `REAL`, `DOUBLE`   |   數字   | 
|   `DECIMAL`   |   預設為字串。如果 `DecimalReturnType` 選項設定為 `DOUBLE_OR_LONG` 則為數字。  | 
|   `STRING`   |   字串   | 
|   `BOOLEAN`, `BIT`   |   Boolean   | 
|   `BLOB`, `BINARY`, `VARBINARY`, `LONGVARBINARY`   |   base64 編碼的字串。  | 
|   `CLOB`   |   字串   | 
|   `ARRAY`   |   陣列   | 
|   `NULL`   |   `null`   | 
|   其他類型 (包含與日期和時間相關的類型)   |   字串   | 

## 故障診斷
<a name="data-api-json-troubleshooting"></a>

 JSON 回應限制為 10 MB。若回應大於此限制，您的程式會收到 `BadRequestException` 錯誤。於此狀況下，您可使用下列其中一種方法解決此錯誤：
+  減少結果集中的列數量。若要執行此作業，請新增 `LIMIT` 子句。您可使用 `LIMIT` 和 `OFFSET` 子句提交多個查詢，將一個較大結果集分割為多個較小的結果集。

   如果結果集包含按應用程式邏輯過濾掉的列，則可以透過在 `WHERE` 子句中新增更多條件來從結果集中移除這些列。
+  減少結果集中的欄數量。若要執行此作業，請從查詢的選取清單中移除項目。
+  在查詢中使用欄別名，來縮短欄標籤。對於結果集中的每一列，每個欄名稱都會在 JSON 字串中重複。因此，具有冗長欄名稱和許多列的查詢結果，可能會超出大小限制。特別是，對複雜的表達式使用欄別名，以避免在 JSON 字串中重複整個表達式。
+  雖然搭配 SQL 可以使用欄別名來產生具有多個相同名稱之欄的結果集，但 JSON 中不允許重複的鍵名稱。如果您請求 JSON 格式的結果集，並且多個欄具有相同的名稱，則 RDS Data API 會傳回錯誤。因此，請確保所有欄標籤都具有唯一的名稱。

## 範例
<a name="data-api-json-examples"></a>

 以下 Java 範例示範如何呼叫 `ExecuteStatement` 並搭配 JSON 格式字串的回應，然後解讀結果集。以適當的值來取代 *databaseName*、*secretStoreArn* 以及 *clusterArn* 參數。

 以下 Java 範例示範了在結果集中傳回十進位數值的查詢。`assertThat` 呼叫測試回應的欄位是否具有根據 JSON 結果集規則的預期屬性。

 此範例適用於以下結構描述和範例資料：

```
create table test_simplified_json (a float);
insert into test_simplified_json values(10.0);
```

```
public void JSON_result_set_demo() {
    var sql = "select * from test_simplified_json";
    var request = new ExecuteStatementRequest()
      .withDatabase(databaseName)
      .withSecretArn(secretStoreArn)
      .withResourceArn(clusterArn)
      .withSql(sql)
      .withFormatRecordsAs(RecordsFormatType.JSON);
    var result = rdsdataClient.executeStatement(request);
}
```

 前面程式中 `formattedRecords` 欄位的值為：

```
[{"a":10.0}]
```

 `Records` 和 `ColumnMetadata` 欄位都為 null，因為存在 JSON 結果集。

 以下 Java 範例示範了在結果集中傳回整數值的查詢。範例會呼叫 `getFormattedRecords` 以僅傳回 JSON 格式的字串，並忽略其他空白或 null 的回應欄位。範例會將結果還原序列化為表示記錄清單的結構。每個記錄都具有其名稱對應於結果集中的欄別名的欄位。此技術簡化了用於剖析結果集的程式碼。您的應用程式不必循環對結果集的列和欄執行迴圈，以及將每個值轉換為適當的類型。

 此範例適用於以下結構描述和範例資料：

```
create table test_simplified_json (a int);
insert into test_simplified_json values(17);
```

```
public void JSON_deserialization_demo() {
    var sql = "select * from test_simplified_json";
    var request = new ExecuteStatementRequest()
      .withDatabase(databaseName)
      .withSecretArn(secretStoreArn)
      .withResourceArn(clusterArn)
      .withSql(sql)
      .withFormatRecordsAs(RecordsFormatType.JSON);
    var result = rdsdataClient.executeStatement(request)
      .getFormattedRecords();

/* Turn the result set into a Java object, a list of records.
   Each record has a field 'a' corresponding to the column
   labelled 'a' in the result set. */
    private static class Record { public int a; }
    var recordsList = new ObjectMapper().readValue(
        response, new TypeReference<List<Record>>() {
        });
}
```

 前面程式中 `formattedRecords` 欄位的值為：

```
[{"a":17}]
```

 若要擷取結果列 0 的 `a` 欄，則應用程式將參考 `recordsList.get(0).a`。

 相比之下，下面的 Java 範例顯示了當您不使用 JSON 格式時，建置包含結果集的資料結構所需的程式碼類型。在這種情況下，結果集的每一列都包含單一使用者相關資訊的欄位。建置表示結果集的資料結構需要在所有列中執行迴圈。對於每一列，程式碼會擷取每個欄位的值、執行適當的類型轉換，並將結果指派給代表列之物件中的相應欄位。然後，程式碼將代表每個使用者的物件新增到表示整個結果集的資料結構中。如果查詢變更為對結果集中的欄位進行重新排序、新增或移除，則應用程式的程式碼也必須變更。

```
/* Verbose result-parsing code that doesn't use the JSON result set format */
for (var row: response.getRecords()) {
    var user = User.builder()
      .userId(row.get(0).getLongValue())
      .firstName(row.get(1).getStringValue())
      .lastName(row.get(2).getStringValue())
      .dob(Instant.parse(row.get(3).getStringValue()))
      .build();
    result.add(user);
  }
```

 下面的範例值顯示了具有不同欄數、欄別名和欄資料類型之結果集的 `formattedRecords` 欄位。

 如果結果集包含多列，則每一列會表示為陣列元素的物件。結果集中的每一欄都將成為物件中的鍵。結果集中的每一列都會重複這些鍵。因此，對於包含許多列和欄的結果集，您可能需要定義簡短的欄別名，以避免超出整個回應的長度限制。

 此範例適用於以下結構描述和範例資料：

```
create table sample_names (id int, name varchar(128));
insert into sample_names values (0, "Jane"), (1, "Mohan"), (2, "Maria"), (3, "Bruce"), (4, "Jasmine");
```

```
[{"id":0,"name":"Jane"},{"id":1,"name":"Mohan"},
{"id":2,"name":"Maria"},{"id":3,"name":"Bruce"},{"id":4,"name":"Jasmine"}]
```

 如果結果集中的欄被定義為表達式，則表達式的文字將成為 JSON 索引鍵。因此，在查詢的選取清單中為每個表達式定義描述性欄別名通常很方便。例如，以下查詢在其選取清單中包括函數呼叫和算術運算等表達式。

```
select count(*), max(id), 4+7 from sample_names;
```

 這些表達式會以索引鍵的形式傳遞給 JSON 結果集。

```
[{"count(*)":5,"max(id)":4,"4+7":11}]
```

 新增具有描述性標籤的 `AS` 欄可讓鍵在 JSON 結果集中更易於解讀。

```
select count(*) as rows, max(id) as largest_id, 4+7 as addition_result from sample_names;
```

 對於修訂後的 SQL 查詢，`AS` 子句定義的欄標籤用作鍵名稱。

```
[{"rows":5,"largest_id":4,"addition_result":11}]
```

 JSON 字串中每個鍵值對的值都可以是帶引號的字串。字串可能包含 Unicode 字元。如果字串包含逸出序列或 `"` 或 `\` 字元，則這些字元前面會有反斜線逸出字元。以下 JSON 字串範例示範了這些可能性。例如，`string_with_escape_sequences` 結果包含特殊字元退格、換行字元、歸位字元、tab 鍵、表單摘要和 `\`。

```
[{"quoted_string":"hello"}]
[{"unicode_string":"邓不利多"}]
[{"string_with_escape_sequences":"\b \n \r \t \f \\ '"}]
```

 JSON 字串中每個鍵值對的值也可以表示一個數字。數字可以是整數、浮點值、負值或以指數表示法表示的值。以下 JSON 字串範例示範了這些可能性。

```
[{"integer_value":17}]
[{"float_value":10.0}]
[{"negative_value":-9223372036854775808,"positive_value":9223372036854775807}]
[{"very_small_floating_point_value":4.9E-324,"very_large_floating_point_value":1.7976931348623157E308}]
```

 布林值和 null 值用未加引號的特殊關鍵字 `true`、`false` 以及 `null` 來表示。以下 JSON 字串範例示範了這些可能性。

```
[{"boolean_value_1":true,"boolean_value_2":false}]
[{"unknown_value":null}]
```

 如果選擇 BLOB 類型的值，則結果將在 JSON 字串中表示為 base64 編碼的值。若要將值轉換回原始表示法，您可以使用應用程式語言中的適當解碼函數。例如，在 Java 中，您可以呼叫函數 `Base64.getDecoder().decode()`。以下範例輸出顯示選擇 BLOB 值 `hello world` 並將結果集作為 JSON 字串傳回。

```
[{"blob_column":"aGVsbG8gd29ybGQ="}]
```

 以下 Python 範例示範如何從呼叫 Python `execute_statement` 函數的結果中存取值。結果集是欄位 `response['formattedRecords']` 中的字串值。程式碼透過呼叫 `json.loads` 函數，將 JSON 字串轉換為資料結構。然後，結果集的每一列都是資料結構中的一個清單元素，而在每一列中，您可以按名稱參考結果集的每個欄位。

```
import json

result = json.loads(response['formattedRecords'])
print (result[0]["id"])
```

 以下 JavaScript 範例示範了如何從呼叫 JavaScript `executeStatement` 函數的結果中存取值。結果集是欄位 `response.formattedRecords` 中的字串值。程式碼透過呼叫 `JSON.parse` 函數，將 JSON 字串轉換為資料結構。然後，結果集的每一列都是資料結構中的一個陣列元素，而在每一列中，您可以按名稱參考結果集的每個欄位。

```
<script>
    const result = JSON.parse(response.formattedRecords);
    document.getElementById("display").innerHTML = result[0].id;
</script>
```