配置Java AWS Lambda使用的Objectmapper

时间:2018-08-10 20:47:33

标签: java spring-boot aws-lambda amazon-sqs

我正在尝试开发由SQS中的事件触发的AWS Lambda函数。

我正在使用 spring-cloud-function-adapter-aws (版本1.0.0.RELEASE),尤其是在 SpringBootRequestHandler 中。

但是,正在使用的ObjectMapper区分大小写,因此无法成功转换来自SQS的Json。

SQS发布以下Json,尤其是我遇到问题的是 Records 字段。

    {
  "Records": [
    {
      "body": "Hello from SQS!",
      "receiptHandle": "MessageReceiptHandle",
      "md5OfBody": "7b270e59b47ff90a553787216d55d91d",
      "eventSourceARN": "arn:aws:sqs:eu-west-1:123456789012:MyQueue",
      "eventSource": "aws:sqs",
      "awsRegion": "eu-west-1",
      "messageId": "19dd0b57-b21e-4ac1-bd88-01bbb068cb78",
      "attributes": {
        "ApproximateFirstReceiveTimestamp": "1523232000001",
        "SenderId": "123456789012",
        "ApproximateReceiveCount": "1",
        "SentTimestamp": "1523232000000"
      },
      "messageAttributes": {}
    }
  ]
}

我已尝试过此问题中的建议,但无济于事。 Configuring ObjectMapper in Spring

在我的POJO中,我还添加了以下注释,但是它不能在Lambda之外运行。

@JsonProperty("Records")
private List<SqsRecord> Records;

任何帮助将不胜感激。

我的Lambda处理程序定义为:

public class SqsEventHandler extends SpringBootRequestHandler<SqsEvent, String> {}

POJO定义为:

public class SqsEvent {

@JsonProperty("Records")
private List<SqsRecord> records;

@Data
public class SqsRecord {
    private String body;
    private String receiptHandle;
    private String md5OfBody;
    private String eventSourceARN;
    private String eventSource;
    private String awsRegion;
    private String messageId;
}

}

我希望示例消息中的Json能够被ObjectMapper读取,但是字段“ records”为空。

3 个答案:

答案 0 :(得分:2)

许多AWS服务都遇到了这个问题。 您必须像这样定义一个新的映射器:

SQSMixin:

 private static interface SQSEventMixin {
    public static final String ATTRIBUTES = "attributes";
    public static final String AWS_REGION = "awsRegion";
    public static final String BODY = "body";
    public static final String EVENT_SOURCE = "eventSource";
    public static final String EVENT_SOURCE_ARN = "eventSourceARN";
    public static final String MD5_OF_BOBY = "md5OfBody";
    public static final String MD5_OF_MESSAGE_ATTRIBUTES = "md5OfMessageAttributes";
    public static final String MESSAGE_ID = "messageId";
    public static final String RECEIPT_HANDLE = "receiptHandle";

    @JsonProperty(value = "Records")
    public List<?> getRecords();

    static interface MessageMixin {
        @JsonProperty(ATTRIBUTES)
        public String getAttributes();

        @JsonProperty(ATTRIBUTES)
        public void setAttributes(String attributes);

        @JsonProperty(AWS_REGION)
        public String getAwsRegion();

        @JsonProperty(AWS_REGION)
        public void setAwsRegion(String awsRegion);

        @JsonProperty(BODY)
        public Object getBody();

        @JsonProperty(BODY)
        public void setBody(Object body);

        @JsonProperty(EVENT_SOURCE)
        public String getEventSource();

        @JsonProperty(EVENT_SOURCE)
        public void setEventSource(String eventSource);

        @JsonProperty(EVENT_SOURCE_ARN)
        public String getEventSourceArn();

        @JsonProperty(EVENT_SOURCE_ARN)
        public void setEventSourceArn(String eventSourceArn);

        @JsonProperty(MD5_OF_BOBY)
        public String getMd5OfBody();

        @JsonProperty(MD5_OF_BOBY)
        public void setMd5OfBody(String md5OfBody);

        @JsonProperty(MD5_OF_MESSAGE_ATTRIBUTES)
        public String getMd5OfMessageAttributes();

        @JsonProperty(MD5_OF_MESSAGE_ATTRIBUTES)
        public void setMd5OfMessageAttributes(String md5OfMessageAttributes);

        @JsonProperty(MESSAGE_ID)
        public String getMessageId();

        @JsonProperty(MESSAGE_ID)
        public void setMessageId(String messageId);

        @JsonProperty(RECEIPT_HANDLE)
        public String getReceiptHandle();

        @JsonProperty(RECEIPT_HANDLE)
        public void setReceiptHandle(String receiptHandle);
    }
}

记录策略:

private static class UpperCaseRecordsPropertyNamingStrategy extends PropertyNamingStrategy.PropertyNamingStrategyBase {

    private static final long serialVersionUID = 1L;

    @Override
    public String translate(String propertyName) {
        if (propertyName.equals("records")) {
            return "Records";
        }
        return propertyName;
    }
}

日期格式器:

private static final DateTimeFormatter dateTimeFormatter = ISODateTimeFormat.dateTime()
        .withZone(new FixedDateTimeZone("GMT", "GMT", 0, 0));

private static class DateTimeMapperModule extends SimpleModule {

    private static final long serialVersionUID = 1L;

    public DateTimeMapperModule() {
        super("DateTimeMapperModule");

        super.addSerializer(DateTime.class, new DateTimeSerializer());
        super.addDeserializer(DateTime.class, new DateTimeDeserializer());
    }
}

private static class DateTimeSerializer extends JsonSerializer<DateTime> {

    @Override
    public void serialize(DateTime value, JsonGenerator gen, SerializerProvider provider) throws IOException {

        gen.writeString(dateTimeFormatter.print(value));
    }
}

private static class DateTimeDeserializer extends JsonDeserializer<DateTime> {

    @Override
    public DateTime deserialize(JsonParser parser, DeserializationContext context) throws IOException {

        return dateTimeFormatter.parseDateTime(parser.getText());
    }
}

并声明您的映射器:

    ObjectMapper mapper = new ObjectMapper();
    mapper.configure(JsonParser.Feature.ALLOW_COMMENTS, true);
    mapper.setPropertyNamingStrategy(new UpperCaseRecordsPropertyNamingStrategy());
    mapper.registerModule(new DateTimeMapperModule());
    mapper.addMixIn(SQSMessage.class, SQSEventMixin.MessageMixin.class);
    SQSEvent request = mapper.convertValue(inputObject, SQSEvent.class);

答案 1 :(得分:1)

我以更简单的方式解决了这个问题。

引用https://docs.aws.amazon.com/lambda/latest/dg/java-handler-io-type-stream.html并具体

  

如果Lambda的序列化方法不能满足您的需求,则可以使用字节流实现

我现在直接使用SpringBootStreamHandler,并在Spring Configuration类中使用所需的配置选项创建了一个ObjectMapper实例,

@Bean
public ObjectMapper objectMapper() {
    final ObjectMapper mapper = new ObjectMapper();
    mapper.configure(MapperFeature.ACCEPT_CASE_INSENSITIVE_PROPERTIES, true);
    mapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
    return mapper;
}

答案 2 :(得分:0)

已经有一个官方库支持这个:https://aws.amazon.com/blogs/opensource/testing-aws-lambda-functions-written-in-java/

<dependency>
  <groupId>com.amazonaws</groupId>
  <artifactId>aws-lambda-java-tests</artifactId>
  <version>1.0.0</version>
  <scope>test</scope>
</dependency>

在你的插件中也有万无一失:

<build>
  <plugins>
    <plugin>
        <groupId>org.apache.maven.plugins</groupId>
        <artifactId>maven-surefire-plugin</artifactId>
        <version>2.22.2</version>
    </plugin>
  </plugins>
</build>

示例:

SQSEvent input = EventLoader.loadEvent("/sqsEvent.json", SQSEvent.class);