起こった問題
RustでLambdaを書いていて、SQSのMessageAttributes
を含むイベントのjsonをうまくデシリアライズできなかったのでメモ。
Error: Error("invalid type: map, expected a string", line: 1, column: 710)
AWSのドキュメントにもjsonのサンプルはあるのですが、肝心のMessageAttributes
の部分が空オブジェクトになっていて中の形式がわかりません。
チュートリアル: Amazon SQS での Lambda の使用 - AWS Lambda
{
"Records": [
{
"messageId": "059f36b4-87a3-44ab-83d2-661975830a7d",
"receiptHandle": "AQEBwJnKyrHigUMZj6rYigCgxlaS3SLy0a...",
"body": "test",
"attributes": {
"ApproximateReceiveCount": "1",
"SentTimestamp": "1545082649183",
"SenderId": "AIDAIENQZJOLO23YVJ4VO",
"ApproximateFirstReceiveTimestamp": "1545082649185"
},
"messageAttributes": {},
"md5OfBody": "098f6bcd4621d373cade4e832627b4f6",
"eventSource": "aws:sqs",
"eventSourceARN": "arn:aws:sqs:us-east-2:123456789012:my-queue",
"awsRegion": "us-east-2"
}
]
}
正しいフォーマットはこう
MessageAttributesを含めたフォーマットはこうです。
{
"Records": [
{
"messageId" : "MessageID_1",
"receiptHandle" : "MessageReceiptHandle",
"body" : "Message Body",
"md5OfBody" : "fce0ea8dd236ccb3ed9b37dae260836f",
"md5OfMessageAttributes" : "582c92c5c5b6ac403040a4f3ab3115c9",
"eventSourceARN": "arn:aws:sqs:us-west-2:123456789012:SQSQueue",
"eventSource": "aws:sqs",
"awsRegion": "us-west-2",
"attributes" : {
"ApproximateReceiveCount" : "2",
"SentTimestamp" : "1520621625029",
"SenderId" : "AROAIWPX5BD2BHG722MW4:sender",
"ApproximateFirstReceiveTimestamp" : "1520621634884"
},
"messageAttributes" : {
"Attribute3" : {
"binaryValue" : "MTEwMA==",
"stringListValues" : ["abc", "123"],
"binaryListValues" : ["MA==", "MQ==", "MA=="],
"dataType" : "Binary"
},
"Attribute2" : {
"stringValue" : "123",
"stringListValues" : [ ],
"binaryListValues" : ["MQ==", "MA=="],
"dataType" : "Number"
},
"Attribute1" : {
"stringValue" : "AttributeValue1",
"stringListValues" : [ ],
"binaryListValues" : [ ],
"dataType" : "String"
}
}
}
]
}
Rustでserdeを使ってデシリアライズするならこういう型になります。
use std::collections::HashMap;
use serde::{Serialize, Deserialize};
#[derive(Debug, Serialize, Deserialize)]
#[serde(rename_all = "PascalCase")]
pub struct SqsEvent {
pub records: Vec<Record>
}
#[derive(Debug, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct Record {
#[serde(default)]
pub message_id: Option<String>,
#[serde(default)]
pub receipt_handle: Option<String>,
#[serde(default)]
pub body: Option<String>,
#[serde(default)]
pub attributes: Option<Attribute>,
#[serde(default)]
pub message_attributes: HashMap<String, MessageAttribute>,
#[serde(default)]
pub md5_of_body: Option<String>,
#[serde(default)]
pub event_source: Option<String>,
#[serde(default)]
pub event_source_a_r_n: Option<String>,
#[serde(default)]
pub aws_region: Option<String>,
}
#[derive(Debug, Serialize, Deserialize)]
#[serde(rename_all = "PascalCase")]
pub struct Attribute {
#[serde(default)]
pub approximate_receive_count: Option<String>,
#[serde(default)]
pub sent_timestamp: Option<String>,
#[serde(default)]
pub sender_id: Option<String>,
#[serde(default)]
pub approximate_first_receive_timestamp: Option<String>,
}
#[derive(Debug, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct MessageAttribute {
#[serde(default)]
pub binary_value: Option<String>,
#[serde(default)]
pub string_value: Option<String>,
#[serde(default)]
pub string_list_values: Option<Vec<String>>,
#[serde(default)]
pub binary_list_values: Option<Vec<String>>,
#[serde(default)]
pub data_type: Option<String>,
}