

# JSON SerDe 库
<a name="json-serde"></a>

在 Athena 中，您可以使用 SerDe 库来将 JSON 数据反序列化。反序列化会转换 JSON 数据，以便它可以被序列化（写出）为不同的格式，如 Parquet 或 ORC。
+ [Hive JSON SerDe](hive-json-serde.md)
+ [OpenX JSON SerDe](openx-json-serde.md) 
+ [Amazon Ion Hive SerDe](ion-serde.md)

**注意**  
Hive 和 OpenX 库期望 JSON 数据位于单行上（未格式化），用新的行字符分隔记录。

由于 Amazon Ion 是 JSON 的超集，因此您可以使用 Amazon Ion Hive SerDe 查询非 Amazon Ion JSON 数据集。与 Hive 和 OpenX JSON SerDe 库不同，Amazon Ion SerDe 不希望每行数据都在一行上。如果您想查询“漂亮打印”格式的 JSON 数据集，或以其他方式采用换行符将字段拆分为一行，则此功能非常有用。

## 库名称
<a name="library-names"></a>

使用以下值之一：

 [org.apache.hive.hcatalog.data.JsonSerDe](https://cwiki.apache.org/confluence/display/Hive/LanguageManual+DDL#LanguageManualDDL-JSON) 

 [org.openx.data.jsonserde.JsonSerDe](https://github.com/rcongiu/Hive-JSON-Serde) 

[com.amazon.ionhiveserde.IonHiveSerDe](https://github.com/amzn/ion-hive-serde)

# Hive JSON SerDe
<a name="hive-json-serde"></a>

Hive JSON SerDe 常用于处理诸如事件之类的 JSON 数据。这些事件表示为用新行分隔的 JSON 编码文本的单行字符串。Hive JSON SerDe 不允许 `map` 或 `struct` 键名称中出现重复的键。

**注意**  
SerDe 期望每个 JSON 文档都位于单行文本中，并且不使用行终止字符分隔记录中的字段。如果 JSON 文本采用美观的打印格式，当您在创建表后尝试对其进行查询时，可能会收到类似以下内容的错误消息：HIVE\$1CURSOR\$1ERROR: Row is not a valid JSON Object（HIVE\$1CURSOR\$1ERROR：行不是有效的 JSON 对象）或 HIVE\$1CURSOR\$1ERROR: JsonParseException: Unexpected end-of-input: expected close marker for OBJECT（HIVE\$1CURSOR\$1ERROR：JsonParseException：意外的输入结束：对象的预期关闭标记）。有关更多信息，请参阅 GitHub 上 OpenX SerDe 文档中的 [JSON 数据文件](https://github.com/rcongiu/Hive-JSON-Serde#json-data-files)。

以下示例 DDL 语句使用 Hive JSON SerDe 基于示例在线广告数据创建表。在 `LOCATION` 子句中，将 `s3://amzn-s3-demo-bucket.elasticmapreduce/samples/hive-ads/tables/impressions` 中的 *myregion* 替换为您运行 Athena 所在的区域标识符（例如 `s3://us-west-2.elasticmapreduce/samples/hive-ads/tables/impressions`）。

```
CREATE EXTERNAL TABLE impressions (
    requestbegintime string,
    adid string,
    impressionid string,
    referrer string,
    useragent string,
    usercookie string,
    ip string,
    number string,
    processid string,
    browsercookie string,
    requestendtime string,
    timers struct
                <
                 modellookup:string, 
                 requesttime:string
                >,
    threadid string, 
    hostname string,
    sessionid string
)   
PARTITIONED BY (dt string)
ROW FORMAT SERDE 'org.apache.hive.hcatalog.data.JsonSerDe'
LOCATION 's3://amzn-s3-demo-bucket.elasticmapreduce/samples/hive-ads/tables/impressions';
```

## 使用 Hive JSON SerDe 指定时间戳格式
<a name="hive-json-serde-timestamp-formats"></a>

要解析字符串中的时间戳值，可以将 `WITH SERDEPROPERTIES` 子字段添加到 `ROW FORMAT SERDE` 子句，然后用它来指定 `timestamp.formats` 参数。在此参数中，指定一个逗号分隔的时间戳模式列表，其中包含一个或多个时间戳模式，如以下示例所示：

```
...
ROW FORMAT SERDE 'org.apache.hive.hcatalog.data.JsonSerDe'
WITH SERDEPROPERTIES ("timestamp.formats"="yyyy-MM-dd'T'HH:mm:ss.SSS'Z',yyyy-MM-dd'T'HH:mm:ss")
...
```

有关更多信息，请参阅 Apache Hive 文档中的 [时间戳](https://cwiki.apache.org/confluence/display/hive/languagemanual+types#LanguageManualTypes-TimestampstimestampTimestamps)。

## 加载用于查询的表
<a name="hive-json-serde-loading-the-table"></a>

创建表后，运行 [MSCK REPAIR TABLE](msck-repair-table.md) 以加载表并使其可从 Athena 进行查询：

```
MSCK REPAIR TABLE impressions
```

## 查询 CloudTrail 日志
<a name="hive-json-serde-querying-cloud-trail-logs"></a>

要查询 CloudTrail 日志，您可以使用 Hive JSON SerDe。有关更多信息以及示例 `CREATE TABLE` 语句，请参阅 [查询 AWS CloudTrail日志](cloudtrail-logs.md)。

# OpenX JSON SerDe
<a name="openx-json-serde"></a>

与 Hive JSON SerDe 一样，您可以使用 OpenX JSON 来处理 JSON 数据。这些数据还表示为用新行分隔的 JSON 编码文本的单行字符串。与 Hive JSON SerDe 一样，OpenX JSON SerDe 不允许 `map` 或 `struct` 键名称中出现重复的键。

## 注意事项和限制
<a name="openx-json-serde-considerations-limitations"></a>
+ 使用 OpenX JSON SerDe 时，结果的数量及其值可能不确定。结果可能包含多于或少于预期的行，或者如果底层数据中没有空值，则结果可能包含意外的空值。要解决此问题，请使用 [Hive JSON SerDe](hive-json-serde.md) 或将数据重写为其他文件格式类型。
+ SerDe 期望每个 JSON 文档都位于单行文本中，并且不使用行终止字符分隔记录中的字段。如果 JSON 文本采用美观的打印格式，当您在创建表后尝试对其进行查询时，可能会收到类似以下内容的错误消息：HIVE\$1CURSOR\$1ERROR: Row is not a valid JSON Object（HIVE\$1CURSOR\$1ERROR：行不是有效的 JSON 对象）或 HIVE\$1CURSOR\$1ERROR: JsonParseException: Unexpected end-of-input: expected close marker for OBJECT（HIVE\$1CURSOR\$1ERROR：JsonParseException：意外的输入结束：对象的预期关闭标记）。

  有关更多信息，请参阅 GitHub 上 OpenX SerDe 文档中的 [JSON 数据文件](https://github.com/rcongiu/Hive-JSON-Serde#json-data-files)。

## 可选属性
<a name="openx-json-serde-optional-properties"></a>

与 Hive JSON SerDe 不同，OpenX JSON SerDe 还具有以下可选的 SerDe 属性，非常适合用于解决数据中的不一致问题。

**use.null.for.invalid.data**  
可选。默认为 `FALSE`。设置为 `TRUE` 时，SerDe 将使用 `NULL` 表示无法反序列化为表架构所定义列类型的列值。  
将 `use.null.for.invalid.data` 设置为 `TRUE` 可能会导致不正确或意外的结果，因为 `NULL` 值会替换架构不匹配的列中的无效数据。我们建议您修复文件或表架构中的数据，而不是启用此属性。启用此属性后，查询不会因无效数据而失败，这可能会阻止您发现数据质量问题。

**ignore.malformed.json**  
可选。设置为 `TRUE` 时，可让您跳过格式错误的 JSON 语法。默认值为 `FALSE`。

**dots.in.keys**  
可选。默认为 `FALSE`。设置为 `TRUE` 时，允许 SerDe 使用下划线替换键名称中的点。例如，如果 JSON 数据集包含名为 `"a.b"` 的键，您可以在 Athena 中使用此属性来定义列名 `"a_b"`。预设情况下（没有此 SerDe），Athena 不允许在列名中使用点。

**case.insensitive**  
可选。默认为 `TRUE`。设置为 `TRUE` 时，SerDe 将所有大写列转换为小写。  
要在数据中使用区分大小写的键名，请使用 `WITH SERDEPROPERTIES ("case.insensitive"= FALSE;)`。然后，对于每个非全部小写的键，请使用以下语法提供从列名到属性名的映射：  

```
ROW FORMAT SERDE 'org.openx.data.jsonserde.JsonSerDe'
WITH SERDEPROPERTIES ("case.insensitive" = "FALSE", "mapping.userid" = "userId")
```
如果您有两个键（例如 `URL` 和 `Url`），并且二者在小写时是相同的，则可能会发生与以下内容类似的错误：  
HIVE\$1CURSOR\$1ERROR: Row is not a valid JSON Object - JSONException: Duplicate key "url"（HIVE\$1CURSOR\$1ERROR：行不是有效的 JSON 对象 - JSONException：重复的键“url”）  
要纠正此错误，请将 `case.insensitive` 属性设置为 `FALSE`，并将键映射到不同的名称，如以下示例所示：  

```
ROW FORMAT SERDE 'org.openx.data.jsonserde.JsonSerDe'
WITH SERDEPROPERTIES ("case.insensitive" = "FALSE", "mapping.url1" = "URL", "mapping.url2" = "Url")
```

**映射**  
可选。将列名映射到与列名不同的 JSON 键。当 JSON 数据包含作为[关键字](reserved-words.md)的键时，`mapping` 参数很有用。例如，如果您有名为 `timestamp` 的 JSON 键，请使用以下语法将该键映射到名为 `ts` 的列：  

```
ROW FORMAT SERDE 'org.openx.data.jsonserde.JsonSerDe'
WITH SERDEPROPERTIES ("mapping.ts" = "timestamp")
```
**将带有冒号的嵌套字段名称映射到与 Hive 兼容的名称**  
如果字段名称的 `struct` 中包含冒号，则可以使用 `mapping` 属性将该字段映射到与 Hive 兼容的名称。例如，假设您的列类型定义包含 `my:struct:field:string`，则可以通过在 `WITH SERDEPROPERTIES` 中加入以下条目来将定义映射到 `my_struct_field:string`：

```
("mapping.my_struct_field" = "my:struct:field")
```
以下示例显示了对应的 `CREATE TABLE` 语句。  

```
CREATE EXTERNAL TABLE colon_nested_field (
item struct<my_struct_field:string>)
ROW FORMAT SERDE 'org.openx.data.jsonserde.JsonSerDe'
WITH SERDEPROPERTIES ("mapping.my_struct_field" = "my:struct:field")
```

## 示例：广告数据
<a name="openx-json-serde-ad-data-example"></a>

以下示例 DDL 语句使用 OpenX JSON SerDe 基于 Hive JSON SerDe 示例中使用的相同示例在线广告数据创建表。在 `LOCATION` 子句中，将 *myregion* 替换为您运行 Athena 的区域的标识符。

```
CREATE EXTERNAL TABLE impressions (
    requestbegintime string,
    adid string,
    impressionId string,
    referrer string,
    useragent string,
    usercookie string,
    ip string,
    number string,
    processid string,
    browsercokie string,
    requestendtime string,
    timers struct<
       modellookup:string, 
       requesttime:string>,
    threadid string, 
    hostname string,
    sessionid string
)   PARTITIONED BY (dt string)
ROW FORMAT SERDE 'org.openx.data.jsonserde.JsonSerDe'
LOCATION 's3://amzn-s3-demo-bucket.elasticmapreduce/samples/hive-ads/tables/impressions';
```

## 示例：反序列化嵌套 JSON
<a name="nested-json-serde-example"></a>

您可以使用 JSON SerDes 来解析更复杂的 JSON 编码数据。这要求使用 `CREATE TABLE` 语句，而这些语句使用 `struct` 和 `array` 元素来表示嵌套结构。

以下示例根据具有嵌套结构的 JSON 数据创建 Athena 表。该示例具有以下结构：

```
{
"DocId": "AWS",
"User": {
        "Id": 1234,
        "Username": "carlos_salazar", 
        "Name": "Carlos",
"ShippingAddress": {
"Address1": "123 Main St.",
"Address2": null,
"City": "Anytown",
"State": "CA"
   },
"Orders": [
   {
     "ItemId": 6789,
     "OrderDate": "11/11/2022" 
   },
   {
     "ItemId": 4352,
     "OrderDate": "12/12/2022"
   }
  ]
 }
}
```

请记住，OpenX SerDe 希望每个 JSON 记录都在一行文本上。当存储在 Amazon S3 中时，前面示例中的所有数据都应该在一行上，如下所示：

```
{"DocId":"AWS","User":{"Id":1234,"Username":"carlos_salazar","Name":"Carlos","ShippingAddress" ...
```

以下 `CREATE TABLE` 语句将 [OpenX-JSONSerde](https://github.com/rcongiu/Hive-JSON-Serde) 与 `struct` 和 `array` 集合数据类型结合使用来为示例数据建立对象组。

```
CREATE external TABLE complex_json (
   docid string,
   `user` struct<
               id:INT,
               username:string,
               name:string,
               shippingaddress:struct<
                                      address1:string,
                                      address2:string,
                                      city:string,
                                      state:string
                                      >,
               orders:array<
                            struct<
                                 itemid:INT,
                                  orderdate:string
                                  >
                              >
               >
   )
ROW FORMAT SERDE 'org.openx.data.jsonserde.JsonSerDe'
LOCATION 's3://amzn-s3-demo-bucket/myjsondata/';
```

要查询表，请使用类似以下示例的 `SELECT` 语句。

```
SELECT 
 user.name as Name, 
 user.shippingaddress.address1 as Address, 
 user.shippingaddress.city as City, 
 o.itemid as Item_ID, o.orderdate as Order_date
FROM complex_json, UNNEST(user.orders) as temp_table (o)
```

要访问结构中的数据字段，示例查询使用点表示法（例如，`user.name`）。要访问结构数组中的数据（如 `orders` 字段），可以使用 `UNNEST` 函数。`UNNEST` 函数将数组扁平化为临时表（在本例中称为 `o`）。这使您可以像处理结构一样使用点表示法来访问未嵌套的数组元素（例如，`o.itemid`）。名称 `temp_table` 在示例中用于说明目的，通常缩写为 `t`。

下表显示了查询结果。


****  

| \$1 | 名称 | 地址 | 城市 | Item\$1ID | Order\$1date | 
| --- | --- | --- | --- | --- | --- | 
| 1 | Carlos | 123 Main St. | Anytown | 6789 | 11/11/2022 | 
| 2 | Carlos | 123 Main St. | Anytown | 4352 | 12/12/2022 | 

## 其他资源
<a name="json-serdes-additional-resources"></a>

有关在 Athena 中使用 JSON 和嵌套 JSON 的更多信息，请参阅以下资源：
+ [使用 JSONSerDe 在 Amazon Athena 中通过嵌套 JSON 和映射来创建表](https://aws.amazon.com/blogs/big-data/create-tables-in-amazon-athena-from-nested-json-and-mappings-using-jsonserde/)（AWS 大数据博客）
+ [我在 Amazon Athena 中尝试读取 JSON 数据时收到错误](https://aws.amazon.com/premiumsupport/knowledge-center/error-json-athena/)（AWS 知识中心文章）
+ [hive-json-schema](https://github.com/quux00/hive-json-schema) (GitHub) – 用 Java 编写的工具，可从示例 JSON 文档生成 `CREATE TABLE` 语句。生成的 `CREATE TABLE` 语句使用 OpenX JSON Serde。