Athena で AWS Config からファイルを読み取るときの「HIVE_CURSOR_ERROR: Row is not a valid JSON Object - JSONException: Duplicate key」を解決する方法を教えてください。

最終更新日: 2019 年 12 月 9 日

Amazon Athena を使用して AWS Config ファイルを照会すると、「Error: HIVE_CURSOR_ERROR: Row is not a valid JSON Object - JSONException: Duplicate key.」というエラーが表示されます。

簡単な説明

このエラーは通常、AWS Config リソースに同じ名前の複数のタグがあり、一部のタグが大文字で、一部が小文字である場合に発生します。たとえば、次のレコードは tc:Name および tc:name JSON キーを使用します。

{
  "fileVersion": "1.0",
  "configSnapshotId": "35eced35-a13a-45b7-81e4-446e35616e70",
  "configurationItems": [
    {
      "tags": { "tc:Name": "6", "tc:name": "abc6-38" }
    },
    {
      "tags": { "tc:Name": "6", "tc:name": "abc6-38" }
    },
    {
      "tags": { "tc:Name": "6" }
    },
   {
      "tags": { "tc:name": "6" }
    }
  ]
}

解決方法

次のような CREATE TABLE ステートメントを実行します。このステートメントは、Athena テーブルを作成し、case.insensitivefalse に設定して、列名と同じではない JSON キーに列名をマッピングします。このステートメントを実行する前に:

  • [LOCATION] フィールドで、 s3://awsexamplebucket/AWSLogs/ を Amazon Simple Storage Service (Amazon S3) バケットの名前に置き換えます。
  • すべてのマッピングプロパティを列名と JSON キー (例: mapping.fileversion'='fileVersion') に置き換えます。
CREATE EXTERNAL TABLE aws_config_configuration_snapshot (
 fileversion STRING,
 configsnapshotid STRING,
 configurationitems ARRAY < STRUCT <
        configurationItemVersion : STRING,
        configurationItemCaptureTime : STRING,
        configurationStateId : BIGINT,
        awsAccountId : STRING,
        configurationItemStatus : STRING,
        resourceType : STRING,
        resourceId : STRING,
        resourceName : STRING,
        ARN : STRING,
        awsRegion : STRING,
        availabilityZone : STRING,
        configurationStateMd5Hash : STRING,
        configuration : STRING,
        supplementaryConfiguration : MAP < STRING, STRING >,
        tags: MAP < STRING, STRING >,
        resourceCreationTime : STRING > >
) 
PARTITIONED BY ( dt STRING , region STRING )
ROW FORMAT SERDE 
 'org.openx.data.jsonserde.JsonSerDe' 
WITH SERDEPROPERTIES ( 
  'case.insensitive'='false',
  'mapping.fileversion'='fileVersion',
  'mapping.configsnapshotid'='configSnapshotId',
  'mapping.configurationitems'='configurationItems',
  'mapping.configurationitemversion'='configurationItemVersion',
  'mapping.configurationitemcapturetime'='configurationItemCaptureTime',
  'mapping.configurationstateid'='configurationStateId',
  'mapping.awsaccountid'='awsAccountId',
  'mapping.configurationitemstatus'='configurationItemStatus',
  'mapping.resourcetype'='resourceType',
  'mapping.resourceid'='resourceId',
  'mapping.resourcename'='resourceName',
  'mapping.arn'='ARN',
  'mapping.awsregion'='awsRegion',
  'mapping.availabilityzone'='availabilityZone',
  'mapping.configurationstatemd5hash'='configurationStateMd5Hash',
  'mapping.supplementaryconfiguration'='supplementaryConfiguration',
  'mapping.configurationstateid'='configurationStateId'
  )
LOCATION 's3://awsexamplebucket/AWSLogs/';

パーティションがロードされたテーブルがすでにある場合は、次のように新しい SerDe プロパティをテーブルに追加できます。

ALTER TABLE aws_config_configuration_snapshot SET TBLPROPERTIES (  
   'case.insensitive'='false',
'mapping.fileversion'='fileVersion',
'mapping.configsnapshotid'='configSnapshotId',
'mapping.configurationitems'='configurationItems',
'mapping.configurationitemversion'='configurationItemVersion',
'mapping.configurationitemcapturetime'='configurationItemCaptureTime',
'mapping.configurationstateid'='configurationStateId',
'mapping.awsaccountid'='awsAccountId',
'mapping.configurationitemstatus'='configurationItemStatus',
'mapping.resourcetype'='resourceType',
'mapping.resourceid'='resourceId',
'mapping.resourcename'='resourceName',
'mapping.arn'='ARN',
'mapping.awsregion'='awsRegion',
'mapping.availabilityzone'='availabilityZone',
'mapping.configurationstatemd5hash'='configurationStateMd5Hash',
'mapping.supplementaryconfiguration'='supplementaryConfiguration',
'mapping.configurationstateid'='configurationStateId')

テーブルの準備ができると、 configurationItem.tags['TAGNAME']を使用してタグにアクセスできます。たとえば、tc:Name タグにアクセスするには、次のクエリを実行します。

select configurationItem.tags['tc:Name']
  from your_table
cross join unnest(configurationItems) as t(configurationItem)
where configurationItem.tags['tc:Name'] is not null