【DynamoDB】boto3 client APIを使用してDynamoDB JSON形式から標準JSON形式へ変換してみた

記事タイトルとURLをコピーする

こんにちは、ディベロップメントサービス1課の山本です。
初めてスプレー形式の日焼け止めを使ったのですが、うまく伸ばせずまだらに日焼けしてしまいました。太陽が憎い。

今回は DynamoDB JSON 形式データの変換方法について説明します。
低レベルAPI(client API)を利用されている方は必見です。

この記事の対象者は?

  • DynamoDB JSON 形式データの処理に困っている人
  • 高レベルAPI(resource API)から低レベルAPI(client API)に移行したい人

DynamoDB JSON 形式データとは

DynamoDB へ 低レベルAPI を利用してアクセスする際に、リクエスト・レスポンスにて利用される JSON 形式となります。
以下のように、データ型がキーとして追加されているのが特徴です。
DynamoDB 低レベル API - Amazon DynamoDB

{
    "Item": {
        "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"}
    }
}

低レベルAPIと高レベルAPI

低レベルAPIと高レベルAPIの違いを簡単にまとめました。

特徴 クライアント(低レベル)API リソースAPI
概要 下層のHTTP API操作との1対1のマッピングを提供 属性にアクセスしアクションを実行するためのリソースオブジェクトとコレクションを提供
boto3 client API resource API

boto3 では resource API の更新が終了しているため、新規で利用する場合は client API を選択するのが良いでしょう。

The AWS Python SDK team does not intend to add new features to the resources interface in boto3. Existing interfaces will continue to operate during boto3’s lifecycle. Customers can find access to newer service features through the client interface.

Resources - Boto3 1.34.137 documentation

進め方

以下ドキュメントの内容に従って、変換用のコードを作成します。
Python と Boto3 による Amazon DynamoDB のプログラミング - Amazon DynamoDB

boto3 に用意されている TypeSerializer および TypeDeserializer クラスを使用して、標準 JSON と DynamoDB JSON を変換します。
boto3.dynamodb.types - Boto3 1.34.137 documentation

変換表

TypeSerializer と TypeDeserializer を用いることで、以下のようにデータの変換が可能です。

データ型 DynamoDB JSON 標準 JSON
NULL — Null {'NULL': True} None
BOOL — ブール {'BOOL': True/False} True/False
N — 数値 {'N': str(value)} Decimal(str(value))
S — 文字列 {'S': string} string
B — バイナリ {'B': bytes} Binary(bytes)
NS — 数値セット {'NS': [str(value)]} set([Decimal(str(value))])
SS — 文字列セット {'SS': [string]} set([string])
BS — バイナリセット {'BS': [bytes]} set([bytes])
L — リスト {'L': list} list
M — マップ {'M': dict} dict

DynamoDB サンプルテーブル

サンプルテーブルを作成しました。
こちらのデータを boto3 から取得します。

属性 データ型 値の例
id(PK) N (数値) 1
UserName S (文字列) John Doe
IsPremiumUser BOOL (ブール) true
ProfileImage B (バイナリ) "test"
SubscriptionIDs NS (数値セット) ["1001", "1002", "1003"]
Interests SS (文字列セット) ["photography", "cooking", "travel"]
Documents BS (バイナリセット) ["test"]
RecentOrders L (リスト) ["order1", "order2", "order3"]
Preferences M (マップ) {"theme": "dark", "language": "en"}

DynamoDB JSON 形式 で頑張るパターン

コード

get_item でキー名を指定する際に、とても面倒です。

import boto3
  
my_session = boto3.Session(profile_name='***')
dynamodb = my_session.client('dynamodb')
  
if __name__ == '__main__':
  
    key = {'id': {'N': '1'}}
    response = dynamodb.get_item(
        TableName='sample_table',
        Key=key
    )
  
    print(response)

レスポンス

レスポンスもキーに型が入っており、非常に扱い辛いです。

{
  "Preferences":{"M":{"theme": {"S": "dark"},"language": {"S": "en"}}},
  "RecentOrders": {"L": [{"S": "order1"},{"S": "order2"},{"S": "order3"}]},
  "IsPremiumUser": {"BOOL": True},
  "Interests": {"SS": ["cooking", "photography", "travel"]},
  "ProfileImage": {"B": b"\xb5\xeb-"},
  "SubscriptionIDs": {"NS": ["1003", "1002", "1001"]},
  "id": {"N": "1"},
  "UserName": {"S": "John Doe"},
  "Documents": {"BS": [b"\xb5\xeb-"]}
}

標準 JSON 形式 の変換を活用するパターン

コード

DynamoDB JSON <->標準 JSON 形式 用の変換関数を2つ追加します。
リクエストパラメーター生成時と、レスポンス変換時に利用します。

import boto3
from boto3.dynamodb.types import TypeDeserializer, TypeSerializer
  
my_session = boto3.Session(profile_name='***')
dynamodb = my_session.client('dynamodb')
  
  
def dynamo_to_python(dynamo_object: dict) -> dict:
    """DynamoDB JSON から 標準 JSON に変換する
    Args:
        dynamo_object (dict): DynamoDB JSON
    Returns:
        dict: 標準 JSON
    """
    deserializer = TypeDeserializer()
    return {
        k: deserializer.deserialize(v)
        for k, v in dynamo_object.items()
    }
  
  
def python_to_dynamo(python_object: dict) -> dict:
    """標準 JSON から DynamoDB JSON に変換する
    Args:
        python_object (dict): 標準 JSON
    Returns:
        dict: DynamoDB JSON
    """
    serializer = TypeSerializer()
    return {
        k: serializer.serialize(v)
        for k, v in python_object.items()
    }
  
  
if __name__ == '__main__':
    key = {"id": 1}  # 標準 JSON で指定
  
    # 標準 JSON から DynamoDB JSON へと変換
    key = python_to_dynamo(key)
  
    response = dynamodb.get_item(
        TableName='sample_table',
        Key=key
    )
  
    # DynamoDB JSON から 標準 JSON へと変換
    response = dynamo_to_python(response['Item'])
  
    print(response)

レスポンス

キー名から型がなくなり、データ処理が抜群にやりやすくなりました。

{
  "Preferences": {"theme": "dark","language": "en"},
  "RecentOrders": ["order1","order2","order3"],
  "IsPremiumUser": True,
  "Interests": {"photography","cooking","travel"},
  "ProfileImage": Binary(b"\xb5\xeb-"),
  "SubscriptionIDs": {Decimal("1001"), Decimal("1002"), Decimal("1003")},
  "id": Decimal("1"),
  "UserName": "John Doe",
  "Documents": {Binary(b"\xb5\xeb-")}
}

まとめ

  • 低レベルAPI (boto3 だと client API)を利用する場合は、入出力が DynamoDB JSON 形式データ となる。
  • boto3 の TypeSerializer と TypeDeserializer を使うことで、標準 JSONへの変換が可能

さいごに

都度["S"]とか["N"]とかつけるのは大変なので、変換して使いましょう。
本ブログがどなかたのお役に立てれば幸いです。

山本 真大(執筆記事の一覧)

アプリケーションサービス部 ディベロップメントサービス1課

2023年8月入社。カピバラさんが好き。