

# JSON SerDe 라이브러리
<a name="json-serde"></a>

Athena에서는 SerDe 라이브러리를 사용하여 JSON 데이터를 역직렬화할 수 있습니다. 역직렬화는 Parquet 또는 ORC와 같은 다른 형식으로 직렬화(기록)할 수 있도록 JSON 데이터를 변환합니다.
+ [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: 행이 유효한 JSON 객체가 아님(HIVE\$1CURSOR\$1ERROR: Row is not a valid JSON Object) 또는 HIVE\$1CURSOR\$1ERROR: JsonParseException: 예기치 않은 입력 종료: OBJECT의 닫기 마커 필요(HIVE\$1CURSOR\$1ERROR: JsonParseException: Unexpected end-of-input: expected close marker for OBJECT) 같은 오류 메시지가 나타날 수 있습니다. 자세한 내용은 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>

문자열에서 타임스탬프 값을 구문 분석하려면 `ROW FORMAT SERDE`절에 `WITH SERDEPROPERTIES` 하위 필드을 추가하고 이를 사용하여 `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 설명서의 [Timestamps](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>

Hive JSON SerDe를 사용하여 CloudTrail 로그를 쿼리할 수 있습니다. 자세한 내용과 예제 `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를 사용하는 경우 결과의 수 및 값이 비결정적일 수 있습니다. 결과에는 예상보다 많거나 적은 수의 행 또는 기본 데이터에 없는 예상치 못한 null 값이 포함될 수 있습니다. 이 문제를 해결하려면 [Hive JSON SerDe](hive-json-serde.md)를 사용하거나 데이터를 다른 파일 형식으로 다시 쓰세요.
+ SerDe는 각 JSON 문서가 레코드의 필드를 구분하는 줄 종료 문자가 없는 한 줄의 텍스트에 있을 것으로 예상합니다. JSON 텍스트가 가독성 좋게 꾸민 형식이면 테이블을 만든 후 쿼리하려고 할 때 HIVE\$1CURSOR\$1ERROR: 행이 유효한 JSON 객체가 아님(HIVE\$1CURSOR\$1ERROR: Row is not a valid JSON Object) 또는 HIVE\$1CURSOR\$1ERROR: JsonParseException: 예기치 않은 입력 종료: OBJECT의 닫기 마커 필요(HIVE\$1CURSOR\$1ERROR: JsonParseException: Unexpected end-of-input: expected close marker for OBJECT) 같은 오류 메시지가 나타날 수 있습니다.

  자세한 내용은 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: 행이 유효한 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 키에 매핑합니다. `mapping` 파라미터는 JSON 데이터에 [키워드](reserved-words.md)인 키가 있을 경우에 유용합니다. 예를 들어 `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 인코딩 데이터를 구문 분석 할 수 있습니다. 이를 위해서는 `struct` 및 `array` 요소를 사용하여 중첩 구조를 나타내는 `CREATE TABLE` 문을 사용해야 합니다.

다음 예제에서는 중첩 구조가 있는 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 | 이름 | 주소 | City | 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 작업에 대한 자세한 내용은 다음 리소스를 참조하세요.
+ [Create tables in Amazon Athena from nested JSON and mappings using JSONSerDe](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 Knowledge Center 문서)
+ [hive-json-schema](https://github.com/quux00/hive-json-schema)(GitHub) – 예제 JSON 문서에서 `CREATE TABLE` 문을 생성하는 Java로 작성된 도구입니다. 생성된 `CREATE TABLE` 문은 OpenX JSON Serde를 사용합니다.