私は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))
このデータを読み取る正しい方法は何ですか?
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
私も同じ問題を抱えていました。これが私が解決した方法です。
「\ n」で分割された行。
input_json_rdd.map(lambda x : re.sub("}{", "}\n{", x, flags=re.UNICODE))
.flatMap(lambda line: line.split("\n"))
ネストされたjsonオブジェクトには複数の「}」があるため、「}」で行を分割しても問題は解決しません。
私は同じ問題を抱えています。
AWS
で区切り文字を設定できればもっと良かったのですが、自分で設定することもできます。
私のユースケースでは、ツイートのストリームを聞いていて、新しいツイートを受け取ったらすぐにFirehose
に入れました。
もちろん、これにより1行のファイルが解析できなくなりました。
そこで、これを解決するために、ツイートのJSONを\n
と連結しました。これにより、ストリームの内容を読み取るときに行を出力し、ファイルを簡単に解析できるいくつかのパッケージを使用できるようになります。
これがお役に立てば幸いです。
これに取り組む最善の方法は、最初に、適切にフォーマットされたjsonファイルを作成し、その中に十分に分離されたjsonオブジェクトを含めることだと思います。私の場合、消防ホースに押し込まれたイベントに「、」を追加しました。次に、ファイルがs3に保存された後、すべてのファイルに、いくつかの区切り文字(この場合はコンマ)で区切られたjsonオブジェクトが含まれます。追加する必要があるもう1つのものは、ファイルの最初と最後にある「[」と「]」です。次に、複数のjsonオブジェクトを含む適切なjsonファイルがあります。それらの解析が可能になります。
この単純な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
変換ラムダを使用して、すべてのレコードの最後に改行を追加しました
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}
角かっこを数えることで、有効な各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行で区切ります。そうすれば、データを1行ずつ簡単に読み取ることができます。そうでない場合は、「}」を区切り文字として使用するスキャナーオブジェクトを作成し、スキャナーを使用して読み取ります。それは仕事をするでしょう。