1
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 1 year has passed since last update.

S3へエクスポートされたDynamoDB JSONを通常のjsonに整形した

Last updated at Posted at 2023-10-19

事象

dynamodbにこのようなデータがある
image.png

dynamodbエクスポートを実施し、S3へエクスポートされたDynamoDB JSONのファイルをダウンロードして解凍したら
image.png

このような形になってしまって、通常のJSONと形が違って、pythonでjsonとして扱いづらい。。。

{"Item":{"id":{"S":"aaaa"},"priority":{"BOOL":false},"address1":{"S":"東京都豊島区"}}
{"Item":{"id":{"S":"bbbbb"},"priority":{"BOOL":false},"longitude":{"N":"140.607"},"latitude":{"N":"34"},"address1":{"S":"神奈川県横浜市青葉区"}}

このように整形したい!

qiita.rb
[
{"id": "aaaa", "priority": "", "address1": "東京都豊島区", "latitude": "", "longitude": ""},
{"id": "bbbbb", "priority": "", "longitude": "140.607", "latitude": "34", "address1": "神奈川県横浜市青葉区"}
]

DynamoDB JSONのままで何が悪い?

DynamoDB JSONの中身は、ただのstr型として扱われる

各行の最後尾にカンマ区切りがなく、dictか配列ではなく、ただのstrとなる
json.loadするときはエラーになる

json.decoder.JSONDecodeError: Extra data:

各項目の先頭には余計に「Item」キーがついてる

Jsonのエラーは出ないが、余計な情報なので消したい

各バリューには余計に「S」とか「N」とかのキーが入った

Jsonのエラーは出ないが、余計な情報なので消したい

数値が全部Decimal型となる

int型やfloat型の値が全部Decimal型となり、jsonとして読み込むときは、TypeErrorが発生
int型かfloat型かstr型、必要に応じて型変換したい

TypeError: Object of type Decimal is not JSON serializable

数値のデータ値が空の場合、そのキーバリューが丸ごと消えてしまう

「アドレス」キーの場合、文字列で空の状態で、 {address2":{"S":""}} としてエクスポートできるが、
「経度」キーや「緯度」キーなど、数値の場合、latitudeとlongitudeのキーバリューが消えてしまった。。。{latitude:{"N":""}}で出力できない。。。

image.png

ブーリアン値はそのままjsonとして扱えない

DynamoDB JSONは {"BOOL":true} の形で出力するので、 {"BOOL":"true"} のようにstr化しないと、jsonとして扱うときJSONDecodeErrorが発生
(今回は業務の都合で、 {"BOOL":"1"} にしたい)

json.decoder.JSONDecodeError: Extra data:

ファイルを読み込むときは、ちゃんとエンコードを指定しないといけない

encoding="utf-8" を指定しないと、UnicodeDecodeErrorが発生

UnicodeDecodeError: 'cp932' codec can't decode byte 0xef

やり方

いろんなサイトを見てソースを作った!
boto3ラブライブのTypeDeserializerで、「S」とか「N」とかを消す処理ができるが、ほかの整形は自分でやらないといけないらしい。。。
(もっといい方法ご存じの方は教えてください~)

import json
from boto3.dynamodb.types import TypeDeserializer
from decimal import Decimal


#str型へ変換。(dynamoDB JSONの数値をDecimal型なので、json.dumpできない)
def from_decimal_to_str(obj):
    if isinstance(obj, Decimal):
        return str(obj)


deserializer = TypeDeserializer()

#エクスポートしたdynamoDB JSONを読み込む。
with open("input.json", "r", encoding="utf-8") as input_json:
    lines = input_json.readlines()

    # ブーリアン値のtrue/falseを文字列化する、"BOOL":false → "BOOL":"","BOOL":true → "BOOL":"1"
    lines = [line.replace("\"BOOL\":false","\"BOOL\":\"\"") for line in lines]
    lines = [line.replace("\"BOOL\":true","\"BOOL\":\"1\"") for line in lines]

    #Itemキーを削除
    lines = [json.loads(line)["Item"] for line in lines]

    #dynamoDB JSONから普通のJSONへ変換、"name":{"S":"aaaa"} → "name":"aaaa"
    lines = [{k: deserializer.deserialize(v) for k, v in line.items()} for line in lines ]

    #latitudeとlongitudeの欠損値を補完(数値が空の場合、dynamoDB JSONはその項目を勝手に削除するため)
    for line in lines:
        line.setdefault("latitude","")
        line.setdefault("longitude","")


    #変換後のファイルを出力
    with open("output.json", "w", encoding="utf-8") as output_json:
        output_json.writelines(json.dumps(lines, ensure_ascii=False, default=from_decimal_to_str))

参考サイト

1
1
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
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?