初級読了 2 分

DynamoDB JSON とマーシャリング

DynamoDB APIから生データを初めて読み取ると、それは入れたJSONのようには見えません。{"status": "open", "priority": 3}のような素のオブジェクトが、{"status": {"S": "open"}, "priority": {"N": "3"}}として返ってきます。すべての値が、その型を名指す1キーのオブジェクトに包まれています。その包み方がDynamoDB JSONであり、それとの相互変換がマーシャリングと呼ばれます。

これはノイズではありません — DynamoDBが通信路上で型を曖昧にしないための方法です。しかし素のJSONを期待する人をつまずかせますし、手書きするとエラーが起きやすいものです。

DynamoDB JSONとは何ですか?

DynamoDB JSONは、DynamoDBが使用する型タグ付きの通信フォーマットです。すべての値は型名を示す1キーのオブジェクトに包まれます — 文字列なら{"S": "open"}、数値なら{"N": "3"}のように。素のJSONとの相互変換はマーシャリングと呼ばれます。素のJSONはセット、バイナリ、または"3"(文字列)と3(数値)の区別を表現できないため、DynamoDB JSONは型を明確に保ちます。

  • DynamoDB JSONはすべての値にその型のタグを付ける — 文字列には{"S": "..."}、数値には{"N": "..."}、といった具合に。
  • マーシャリング = 素のJSON → DynamoDB JSON。アンマーシャリング = その逆。
  • 数値は通信路上では文字列{"N": "3"}であって{"N": 3}ではない — 精度を保つため。
  • 型タグは、あなたがすでにモデリングしているデータ型システム そのもの:S, N, B, BOOL, NULL, L, M, SS, NS, BS。
  • 手で書かない。 SDKのドキュメントクライアント(またはコンバーター)が代わりにマーシャルします。手作業で行うのはデバッグや式の組み立てのときだけにしましょう。

問題:素のJSONでは足りない

JSONにはちょうど3つのスカラー種別 — 文字列、数値、ブール値 — に加えてnull、配列、オブジェクトがあります。DynamoDBにはもっとあります:バイナリと、JSONがまったく表現できない3つのセット型(文字列セット、数値セット、バイナリセット)です。JSONはまた、文字列の"3"と数値の3、あるいはリストとセットを区別できません。

そのためDynamoDBはあなたのJSONをそのまま保存できません — 各値の正確な型が明示的に述べられている必要があります。型記述子が、それをすべてのリクエストとレスポンスで損失なく行う方法です。

エンコードの仕組み

すべての属性値は、キーが型記述子である単一キーのオブジェクトになります:

記述子
S文字列{"S": "open"}
N数値(文字列として){"N": "3"}
Bバイナリ{"B": "dGV4dA=="}
BOOLブール値{"BOOL": true}
NULLNull{"NULL": true}
Lリスト{"L": [{"S": "a"}, {"N": "1"}]}
Mマップ{"M": {"k": {"S": "v"}}}
SS / NS / BS文字列 / 数値 / バイナリのセット{"SS": ["a", "b"]}

リストとマップは同じ記述子を最後まで下までネストするため、深く構造化されたアイテムは深く包まれます。数値があえて通信路上を文字列として進むのは、JSONの数値(IEEE-754のdouble)なら静かに丸めてしまう任意精度を、DynamoDBが保てるようにするためです。これらはあなたがモデリングするデータ型そのものです。DynamoDB JSONは、その明示的な通信路上の形にすぎず、AWS低レベルAPIリファレンスに定義されています。

実例:監査ログのエントリ

アプリ内で書く素のJSON:

{
  "actor": "u-204",
  "action": "ticket.close",
  "ticketId": 8842,
  "tags": ["billing", "urgent"],
  "redacted": false
}

API向けにDynamoDB JSONへマーシャルしたもの:

{
  "actor": {"S": "u-204"},
  "action": {"S": "ticket.close"},
  "ticketId": {"N": "8842"},
  "tags": {"SS": ["billing", "urgent"]},
  "redacted": {"BOOL": false}
}

マーシャラーが下した選択に注目してください。ticketId文字列値を持つNになり、tagsはリストではなく文字列セット(SS)になりました — セットは重複を除き順序を持たないため、意図的な決定です。tagsSSであるべきかLであるべきかは、コンバーターが代わりに下せないモデリング上の判断であり、まさにそれがエンコードを理解することの重要性です。

DynoTableで変換する

これを手で読み書きする必要はめったにありません。素のJSONをDynamoDB JSONコンバーターに貼り付けてマーシャル(およびその逆)し、リクエストを組み立てるときにはDynamoDB Expression Builderが、式とともに正しくマーシャルされた属性値マップを出力します。アプリ自体では、DynoTableがアイテムを素の読みやすい値として表示し、書き込み時に代わりにマーシャルします。

DynoTableがアイテムを素の値として表示し、生のDynamoDB JSONも利用できる様子。
DynoTableがアイテムを素の値として表示し、生のDynamoDB JSONも利用できる様子。

落とし穴と次のステップ

  • DynamoDB JSONでは数値は文字列{"N": "3"}。引用符が重要です。素の数値を出力しないでください。
  • セットかリストかはモデリング上の決定 で、エンコードがそれを可視化します — 意図的に選びましょう(データ型を参照)。
  • アプリコードでは手作業のマーシャリングより、SDKのドキュメントクライアントを優先する。 手作業のDynamoDB JSONはデバッグと式のために取っておきましょう。
  • 空文字列は許可されています が、歴史的にツールをつまずかせてきました — エッジケースを検証しましょう。

型タグを目で解読する代わりに、アイテムを素の値として閲覧したいですか? DynoTableをダウンロードして、データを直接扱ってください。

更新日