web-dev-qa-db-ja.com

Amazon KinesisFirehoseストリームによってs3に書き込まれたデータを読み取る

私はKinesisFirehoseストリームにレコードを書き込んでいますが、最終的にはAmazon KinesisFirehoseによってS3ファイルに書き込まれます。

私のレコードオブジェクトは次のようになります

ItemPurchase {
    String personId,
    String itemId
}

S3に書き込まれるデータは次のようになります。

{"personId":"p-111","itemId":"i-111"}{"personId":"p-222","itemId":"i-222"}{"personId":"p-333","itemId":"i-333"}

カンマの分離はありません。

Jsonアレイのように開始ブラケットはありません

[

Jsonアレイのように終わりのないブラケット

]

このデータを読みたいのですが、ItemPurchaseオブジェクトのリストを取得します。

List<ItemPurchase> purchases = getPurchasesFromS3(IOUtils.toString(s3ObjectContent))

このデータを読み取る正しい方法は何ですか?

15
learner_21

Amazon Firehoseがこの方法でJSONメッセージをS3にダンプし、区切り文字などを設定できないことに気が遠くなります。

最終的に、この問題に対処するために私が見つけたトリックは、JSONraw_decodeメソッドを使用してテキストファイルを処理することでした。

これにより、連結されたJSONレコードの束を、区切り文字なしで読み取ることができます。

Pythonコード:

import json

decoder = json.JSONDecoder()

with open('giant_kinesis_s3_text_file_with_concatenated_json_blobs.txt', 'r') as content_file:

    content = content_file.read()

    content_length = len(content)
    decode_index = 0

    while decode_index < content_length:
        try:
            obj, decode_index = decoder.raw_decode(content, decode_index)
            print("File index:", decode_index)
            print(obj)
        except JSONDecodeError as e:
            print("JSONDecodeError:", e)
            # Scan forward and keep trying to decode
            decode_index += 1
8
Tom Chapin

私も同じ問題を抱えていました。これが私が解決した方法です。

  1. 「} {」を「}\n {」に置き換えます
  2. 「\ n」で分割された行。

    input_json_rdd.map(lambda x : re.sub("}{", "}\n{", x, flags=re.UNICODE))
                  .flatMap(lambda line: line.split("\n"))
    

ネストされたjsonオブジェクトには複数の「}」があるため、「}」で行を分割しても問題は解決しません。

5
Xuehua Jiang

私は同じ問題を抱えています。

AWSで区切り文字を設定できればもっと良かったのですが、自分で設定することもできます。

私のユースケースでは、ツイートのストリームを聞いていて、新しいツイートを受け取ったらすぐにFirehoseに入れました。

もちろん、これにより1行のファイルが解析できなくなりました。

そこで、これを解決するために、ツイートのJSONを\nと連結しました。これにより、ストリームの内容を読み取るときに行を出力し、ファイルを簡単に解析できるいくつかのパッケージを使用できるようになります。

これがお役に立てば幸いです。

3
johni

これに取り組む最善の方法は、最初に、適切にフォーマットされたjsonファイルを作成し、その中に十分に分離されたjsonオブジェクトを含めることだと思います。私の場合、消防ホースに押し込まれたイベントに「、」を追加しました。次に、ファイルがs3に保存された後、すべてのファイルに、いくつかの区切り文字(この場合はコンマ)で区切られたjsonオブジェクトが含まれます。追加する必要があるもう1つのものは、ファイルの最初と最後にある「[」と「]」です。次に、複数のjsonオブジェクトを含む適切なjsonファイルがあります。それらの解析が可能になります。

2

ファイアホースの入力ソースがAnalyticsアプリケーションの場合、この区切り文字なしの連結JSONは、引用されている既知の問題です ここ 。 JSONオブジェクトを複数行で出力する here のようなラムダ関数が必要です。

2
user2661738

この単純なPythonコードを使用してください。

input_str = '''{"personId":"p-111","itemId":"i-111"}{"personId":"p-222","itemId":"i-222"}{"personId":"p-333","itemId":"i-333"}'''

data_str = "[{}]".format(input_str.replace("}{","},{"))
data_json = json.loads(data_str)

そして(必要に応じて)パンダに変換します。

import pandas as pd   
df = pd.DataFrame().from_records(data_json)
print(df)

そしてこれが結果です

itemId personId
0  i-111    p-111
1  i-222    p-222
2  i-333    p-333
1
Jozef Cechovsky

変換ラムダを使用して、すべてのレコードの最後に改行を追加しました

def lambda_handler(event, context):
    output = []

    for record in event['records']:

        # Decode from base64 (Firehose records are base64 encoded)
        payload = base64.b64decode(record['data'])

        # Read json as utf-8    
        json_string = payload.decode("utf-8")

        # Add a line break
        output_json_with_line_break = json_string + "\n"

        # Encode the data
        encoded_bytes = base64.b64encode(bytearray(output_json_with_line_break, 'utf-8'))
        encoded_string = str(encoded_bytes, 'utf-8')

        # Create a deep copy of the record and append to output with transformed data
        output_record = copy.deepcopy(record)
        output_record['data'] = encoded_string
        output_record['result'] = 'Ok'

        output.append(output_record)

    print('Successfully processed {} records.'.format(len(event['records'])))

    return {'records': output}
1
user2416162

角かっこを数えることで、有効な各JSONを見つけることができます。ファイルが{で始まると仮定すると、このpythonスニペットは機能するはずです:

import json

def read_block(stream):
    open_brackets = 0
    block = ''
    while True:
        c = stream.read(1)
        if not c:
            break

        if c == '{':
            open_brackets += 1
        Elif c == '}':
            open_brackets -= 1

        block += c

        if open_brackets == 0:
            yield block
            block = ''


if __name__ == "__main__":
    c = 0
    with open('firehose_json_blob', 'r') as f:
        for block in read_block(f):
            record = json.loads(block)
            print(record)
1
Rafael Barbosa

データの書き込み方法を変更する方法がある場合は、すべてのレコードを1行で区切ります。そうすれば、データを1行ずつ簡単に読み取ることができます。そうでない場合は、「}」を区切り文字として使用するスキャナーオブジェクトを作成し、スキャナーを使用して読み取ります。それは仕事をするでしょう。

0
psychorama