LoginSignup
1
0

More than 1 year has passed since last update.

DynamoDBデータ型記述子付きJSONをPOJOに変換する方法

Last updated at Posted at 2022-01-05

ユースケース

Amazon DynamoDB の変更データキャプチャを通したり、Amazon Kinesis Data FirehoseでS3保存したりするとDynamoDB 低レベル API のフォーマットで取り扱うことになります。

低レベルフォーマット例
{
    "Age": {"N": "8"},
    "Colors": {
        "L": [
            {"S": "White"},
            {"S": "Brown"},
            {"S": "Black"}
        ]
    },
    "Name": {"S": "Fido"},
    "Vaccinations": {
        "M": {
            "Rabies": {
                "L": [
                    {"S": "2009-03-17"},
                    {"S": "2011-09-21"},
                    {"S": "2014-07-08"}
                ]
            },
            "Distemper": {"S": "2015-10-13"}
        }
    },
    "Breed": {"S": "Beagle"},
    "AnimalType": {"S": "Dog"}
}

しかし、JavaではSDKよりDynamoDBMapperという高レベルAPIが提供されており、それに合わせてPOJOを用意してることが多いかと思います。
低レベルAPIのフォーマットは「JSONなので、、、」と安直にJackson等でこのPOJOをそのまま使おうとすると「データ型記述子(en:Data Type Descriptors)]」が邪魔になり扱えません。
そういった状況への対応の1つです。

方法

DynamoDBMapperで使える形式に変換してから渡す。

注釈

  • 今回は単一レコードを処理対象とします。
  • SomePOJO.classはDynamoDBMapperで使えるPOJOとして定義されてるものとします。
  • dynamoDBMapperは任意のconfigを施したものを定義したものとします。
import類
import static com.amazonaws.protocol.json.SdkStructuredPlainJsonFactory.JSON_CUSTOM_TYPE_UNMARSHALLERS;
import static com.amazonaws.protocol.json.SdkStructuredPlainJsonFactory.JSON_FACTORY;
import static com.amazonaws.protocol.json.SdkStructuredPlainJsonFactory.JSON_SCALAR_UNMARSHALLERS;

import java.io.ByteArrayInputStream;
import java.io.InputStream;
import java.util.Map;

import com.amazonaws.services.dynamodbv2.datamodeling.DynamoDBMapper;
import com.amazonaws.services.dynamodbv2.model.AttributeValue;
import com.amazonaws.services.dynamodbv2.model.transform.AttributeValueJsonUnmarshaller;
import com.amazonaws.transform.JsonUnmarshallerContext;
import com.amazonaws.transform.JsonUnmarshallerContextImpl;
import com.amazonaws.transform.MapUnmarshaller;
import com.fasterxml.jackson.core.JsonParser;
処理部(メソッドの中)
//①
InputStream src = new ByteArrayInputStream("生JSON".getBytes());

//②
JsonParser jsonParser = JSON_FACTORY.createParser(src);

//③
JsonUnmarshallerContext context = new JsonUnmarshallerContextImpl(
    jsonParser,
    JSON_SCALAR_UNMARSHALLERS, JSON_CUSTOM_TYPE_UNMARSHALLERS,
    null
);

//④
MapUnmarshaller<String, AttributeValue> mapUnmarshaller = new MapUnmarshaller<>(
    context.getUnmarshaller(String.class),
    AttributeValueJsonUnmarshaller.getInstance()
);

//==========================================================
//⑤
Map<String, AttributeValue> itemAttributes = mapUnmarshaller.unmarshall(context);

//⑥
SomePOJO pojo = dynamoDBMapper.marshallIntoObject(SomePOJO.class, itemAttributes);

解説

大きく分けて2段階の⑤⑥とその準備の①~④に分かれる。

①ソースの準備

低レベルフォーマットを任意のInputStreamで用意する。
べた書きなら例にもあるようにByteArrayInputStream
ファイルから読むならFiles#newInputStreamなど

②パーサーの準備

パーサーへ①で作成したInputStreamを入力する。
この際、パーサーは使い捨てとなるので注意

③コンテキストの作成

②で用意したデータと定数を入力する。
この定数はcom.amazonaws.http.JsonResponseHandlerのsimpleTypeUnmarshallersとcustomTypeMarshallersの定義を逆引きしていくと記載の定数にたどり着く
第四引数は本来com.amazonaws.http.HttpResponseだが⑤の一次変換を行う上では使われない。

④Unmarshallerの準備

前段で使う変換定義となる。
今回はcom.amazonaws.services.dynamodbv2.model.transform.QueryResultJsonUnmarshallerの記述と合わせておく
なお、contextは前述のsimpleTypeUnmarshallersで固定されているため、contextを使用せずJSON_SCALAR_UNMARSHALLERSから直接記述しても同様の効果となると思われる。

⑤一次変換

dynamoDBMapperへ入力できるクラスへ変換する。
①-⑤まではすべてこのための準備となる。

⑥二次変換(POJO)

変換先POJOクラスと一次変換の結果を渡す。

リンク

1
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
0