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

18,703

Solution 1

It worked for me in all the scenarios when I change the type of input argument from String to 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;
  }
}

************************** Added on 12 March 2021 ***************************

After working on a couple of Lambda implementations, I realized that the input argument is nothing but a plain string representation of a JSON structure or a Map<String, Object> representation. For the map representation, Key is the name of the attribute, and the value is (1) a String if it is a primitive value or (2) a List if it has multiple values, is another Map<String, Object> or another JSON structure. You can recover the JSON representation with:

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

Solution 2

This is an error message during Lambda deserialization.

Your API Gateway mapping template is sending a JSON object, but your handler is expecting a String. Either send a raw string from API Gateway, or update your handler to use a POJO corresponding to your template output.

i.e.

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

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

Solution 3

I tried with Object as parameter type as well as Pojo class and it worked in certain scenarios but while making a request from browser with API gateway URL, it failed and gave exact above error. Spent at least 2-3 hours to figure out that correct Signature, which would work in most cases is below. However this is for hello world example, you would obviously customize your input as per your requirement.

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

    }
}

Solution 4

InputStream should be able to handle any input.

Reference: 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) {
Share:
18,703
Ondrej Tokar
Author by

Ondrej Tokar

A dreamer coming from software development and marketing environment with a vision to bring "magic"to reality through immersive gameplay and advanced technology.

Updated on June 14, 2022

Comments

  • Ondrej Tokar
    Ondrej Tokar almost 2 years

    I have made a Lambda Function and I want to access it via URL with a help of the API Gateway.

    I have set it all up, I have also created an application/json body mapping template in API Gateway looking like this:

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

    And then I am triggering HTTP GET request that looks like this:

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

    My Java handler class looks like this:

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

    And this is the full error message:

    {
      "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)"
          ]
        }
      }
    }