AWS 无法反序列化 START_OBJECT 中 java.lang.String 的实例

AWS Can not deserialize instance of java.lang.String out of START_OBJECT

我创建了一个 Lambda 函数,我想在 API 网关的帮助下通过 URL 访问它。

我已经全部设置好,我还在 API 网关中创建了一个 application/json body 映射模板,如下所示:

{ 
    "input": "$input.params('input')",
}

然后我触发了如下所示的 HTTP GET 请求:

https://dmquh95ckh.execute-api.eu-west-1.amazonaws.com/prod/OtoTestFunction?input=test

我的 Java 处理程序 class 看起来像这样:

public class LambdaFunctionHandler implements RequestHandler<String, String> {

    @Override
    public String handleRequest(String input, Context context) {
        context.getLogger().log("Input: " + input);
        return "Test completed."+input;
    }
}

这是完整的错误信息:

{
  "errorMessage": "An error occurred during JSON parsing",
  "errorType": "java.lang.RuntimeException",
  "stackTrace": [],
  "cause": {
    "errorMessage": "com.fasterxml.jackson.databind.JsonMappingException: Can not deserialize instance of java.lang.String out of START_OBJECT token\n at [Source: lambdainternal.util.NativeMemoryAsInputStream@68c4039c; line: 1, column: 1]",
    "errorType": "java.io.UncheckedIOException",
    "stackTrace": [],
    "cause": {
      "errorMessage": "Can not deserialize instance of java.lang.String out of START_OBJECT token\n at [Source: lambdainternal.util.NativeMemoryAsInputStream@68c4039c; line: 1, column: 1]",
      "errorType": "com.fasterxml.jackson.databind.JsonMappingException",
      "stackTrace": [
        "com.fasterxml.jackson.databind.JsonMappingException.from(JsonMappingException.java:148)",
        "com.fasterxml.jackson.databind.DeserializationContext.mappingException(DeserializationContext.java:835)",
        "com.fasterxml.jackson.databind.deser.std.StringDeserializer.deserialize(StringDeserializer.java:59)",
        "com.fasterxml.jackson.databind.deser.std.StringDeserializer.deserialize(StringDeserializer.java:12)",
        "com.fasterxml.jackson.databind.ObjectReader._bindAndClose(ObjectReader.java:1441)",
        "com.fasterxml.jackson.databind.ObjectReader.readValue(ObjectReader.java:1047)"
      ]
    }
  }
}

这是 Lambda 反序列化期间的错误消息。

您的 API 网关映射模板正在发送一个 JSON 对象,但您的处理程序需要一个字符串。从 API 网关发送原始字符串,或更新您的处理程序以使用与您的模板输出对应的 POJO。

public class MyPojo {
   private String input;
   public String getInput() { return input; }
   public void setInput(String input) { this.input = input; }
}

参见: http://docs.aws.amazon.com/lambda/latest/dg/java-programming-model-req-resp.html

当我将输入参数的类型从 String 更改为 Object.

时,它在所有情况下都对我有用
public class LambdaFunctionHandler implements RequestHandler<Object, String> {

  @Override
  public String handleRequest(Object input, Context context) {
    String data= input != null ? input.toString() : "{}";
    context.getLogger().log("Input: " + data);
    return "Test completed."+data;
  }
}

************************** 添加于 2021 年 3 月 12 日 ************** *************

在研究了几个 Lambda 实现之后,我意识到输入参数只不过是 JSON 结构或 Map 表示的纯字符串表示。 对于地图表示,Key 是属性的名称,值是 (1) 如果它是原始值,则为 String 或 (2) 如果它有多个值,则为 List,是另一个 Map 或另一个JSON 结构。您可以使用以下方法恢复 JSON 表示:

    if(input instanceof String)
    {
        String lambdaInputJsonStr = (String)input;
    }
    else if(input instanceof Map)
    {
        String lambdaInputJsonStr = gson.toJson((Map)input);
    }

我尝试使用 Object 作为参数类型以及 Pojo class,它在某些情况下工作,但是当使用 API 网关 URL 从浏览器发出请求时,它失败了并且给出了准确的上述错误。至少花了 2-3 个小时来找出正确的签名,在大多数情况下都可以使用,如下所示。然而,这是针对 hello world 示例,您显然会根据您的要求自定义输入。

public class LambdaFunctionHandler implements RequestHandler<***Map<String,Object>,***  Customer> { 
    @Override
    public Customer handleRequest(***Map<String,Object> input***, Context context) {

    }
}

InputStream 应该能够处理任何输入。

参考: https://docs.aws.amazon.com/lambda/latest/dg/java-handler.html

InputStream – The event is any JSON type. The runtime passes a byte stream of the document to the handler without modification. You deserialize the input and write output to an output stream.

public class Handler implements RequestHandler<InputStream, String> {
@Override
    public String handleRequest(InputStream event, Context context) {

错误消息试图说“由于 input 是一个字符串,我希望它以引号 " 开头。但我看到的是 { ”。这就是为什么将 input 类型更改为 ObjectMap<String,Object>MyPojo 让解析器满意的原因。

如果 input 确实应该是一个字符串,则负载值本身必须以 " 开头。例如,String payload = "\"string input\"".

正如其他人所提到的,出现错误是因为 JSON 请求无法转换为 String 对象。您可以使用自定义输入参数 class 来实现您的处理程序,例如:

    public class ZinusoftLambdaHandler  implements 
     RequestHandler<CustomGetEventInput,String> {
        public String handleRequest(CustomGetEventInput input, Context context) {
        context.getLogger().log("Hello World from ZinusoftLambdaHandler : 
        "+input.getValue());
        return "Response from lambda "+input.getValue();
     }
   }

您的 CustomGetEventInput class 只是一个简单的 POJO class。

为了调用 API 中的 POST 方法,您需要创建一个模型并将其添加到 API 定义的方法请求部分的请求正文中.模型的 json 定义需要与 CustomGetEventInput class.

的定义相匹配

例如

public class CustomEventInput {
    private List<Integer> values;

    public CustomEventInput() {     
    }
    
    public CustomEventInput(List<Integer> input) {
        values = input;
    }
    
    public List<Integer> getValues() {
        return values;
    }

    public void setValues(List<Integer> values) {
        this.values = values;
    }
}

JSON 型号

{
  "$schema": "http://json-schema.org/draft-04/schema#",
  "title": "CustomEventInput",
  "type": "object",
  "properties": {
    "values": {
      "type": "array",
      "items": { "type": "integer" }
    }
  }
}

对于 GET 方法,您需要使用传递给 lambda 函数的参数名称定义一个 URL 查询字符串。