冥冥乃志

ソフトウェア開発会社でチームマネージャをしているエンジニアの雑記。アウトプットは少なめです。

follow us in feedly

AWS Glueのお試し:JSONを扱う時に注意事項があるっぽい

本当はもっと先のところまでやりたかったんですが、ちょっといくつか引っかかっているポイントがあって、一つわかったことがあるのでそこだけ先出しします。AWS GlueとAthenaでJSONを取り扱う時の注意事項の話。というか現象はわかったけど、どのアプローチが仕様的に正しいんだっけ?というのは未だわからず。

JSONファイルのクローラは作れたがジョブでデータを扱えない

前々回のエントリで、JSONファイルのテーブル定義を取得するクローラを作ることはできました。

で、これを普通にParquet形式に受け流すだけのジョブを作って実行して見たんですが、以下のようなエラー吐いてこけます。

: org.apache.spark.SparkException: Job aborted due to stage failure: Task 0 in stage 0.0 failed 4 times, most recent failure: Lost task 0.3 in stage 0.0 (TID 3, ip-172-31-19-251.ec2.internal, executor 1): com.fasterxml.jackson.core.JsonParseException: Unexpected character (',' (code 44)): expected a valid value (number, String, array, object, 'true', 'false' or 'null')

カンマのパースでこけてますね。もともと適当にGithubを探していて見つけたJSONファイルで、クローラを作る時にもカンマ区切りのオブジェクトの羅列になるように手を入れています。このオブジェクトの区切り方がおかしいということらしいです。

というわけで、レコードのオブジェクトの区切りをなくす、具体的には }, から } に変えてみることでジョブを実行することができました。返還後のParquetファイルをAthenaで検索することもできています。Athenaに投げたDDLは以下の通り。

CREATE EXTERNAL TABLE IF NOT EXISTS flights.airport_target_test (
  iata string,
  lon string,
  iso string,
  status int,
  name string,
  continent string,
  type string,
  lat string,
  size string
)
ROW FORMAT SERDE 'org.apache.hadoop.hive.ql.io.parquet.serde.ParquetHiveSerDe'
WITH SERDEPROPERTIES (
  'serialization.format' = '1'
) LOCATION 's3://***/'

Athenaで元データを検索してみる

それはそうとAthenaで変換元のJSONファイルを検索しようとして見たら、

HIVE_CURSOR_ERROR: Row is not a valid JSON Object - JSONException: A JSONObject text must end with '}' at 2 [character 3 line 1]

というエラーになって検索できません。どうにもJSONオブジェクトは } で終われ、と言われているようですが、そうなってるんですよね。

で、色々試した結果、おそらくAthenaとGlueで使っているJSONパーサーが違うんじゃないかという挙動です。元データは、以下のように「人間にとって」可読性がある程度高くなるようにカラムごとの改行を入れたデータでした。これで、Glueのクローラとジョブは実行できます。ジョブで変換したParquetファイルを検索しても、データはきちんと認識している模様。

{
    "iata": "UTK",
    "lon": "169.86667",
    "iso": "MH",
    "status": 1,
    "name": "Utirik Airport",
    "continent": "OC",
    "type": "airport",
    "lat": "11.233333",
    "size": "small"
}
{
    "iata": "FIV",
    "iso": "US",
    "status": 1,
    "name": "Five Finger CG Heliport",
    "continent": "NA",
    "type": "heliport",
    "size": null
}
...

Athenaでも検索可能なJSONファイルは以下で、1行1レコードになっていないとエラーになるパーサのようです。

{"iata": "UTK", "lon": "169.86667", "iso": "MH", "status": 1, "name": "Utirik Airport", "continent": "OC", "type": "airport", "lat": "11.233333", "size": "small"}
{"iata": "FIV", "iso": "US", "status": 1, "name": "Five Finger CG Heliport", "continent": "NA", "type": "heliport", "size": null}
...

あー、面倒臭い。

まとめ

まずこういうケースでバルクデータっぽく使われる場合のJSONの仕様ってあったっけ?というのがドキュメント読んでもよくわからないのと、GlueとAthenaのパースの扱いが違うのとか割と問題に気づくまでが面倒臭いのとか考えると、特に強い理由がなければCSVでもいいんじゃないのかなあ?というのが正直な感想です。CSVにしたくなければそのままJDBCをデータソースやターゲットに使うという方法もありますし。バルクデータという観点で行けば、CSVの方がデータの構成上サイズは小さくなりそうなので。