Skip to content
Advertisement

Convert from object to AWS Kinesis -> Java Cannot deserialize instance of `java.util.Date` out of VALUE_NUMBER_FLOAT token

I am working on a AWS lambda function in order to get dynamically a SNS Event or a Kinesis Event, to do so, in my lambda function I am getting an Object as parameter:

@Override
public GatewayResponse handleRequest(final Object object, Context context)

Then, I am using Jackson in order to convert it to SNS or Kinesis dynamically:

public static SNSEvent convertObjectToSNSEvent(Object object) {
    return objectMapper.convertValue(object, SNSEvent.class);
}

public static KinesisEvent convertToKinesisEvent(Object object) {
    return objectMapper.convertValue(object, KinesisEvent.class);
}

The problem is that, when I’m using the convertToKinesisEvent function I am getting:

java.lang.IllegalArgumentException: Cannot deserialize instance of `java.util.Date` out of VALUE_NUMBER_FLOAT token

SNSEvent convertion works fine, but Kinesis does not, I have realized that is because in the Kinesis Json, the time stamp (approximateArrivalTimestamp field) is represented with decimals:

{
  "Records": [
    {
      "kinesis": {
        "partitionKey": "partitionKey-03",
        "kinesisSchemaVersion": "1.0",
        "data": "eyJpZCI6MjY2MjU4NSwiZGVzY3JpcHRpb24iOiJUZXN0IFBheWxvYWQifQ==",
        "sequenceNumber": "49545115243490985018280067714973144582180062593244200961",
        "approximateArrivalTimestamp": 1.57538737506E9
      },
      "eventSource": "aws:kinesis",
      "eventID": "shardId-000000000000:49545115243490985018280067714973144582180062593244200961",
      "invokeIdentityArn": "arn:aws:iam::EXAMPLE",
      "eventVersion": "1.0",
      "eventName": "aws:kinesis:record",
      "eventSourceARN": "arn:aws:kinesis:EXAMPLE",
      "awsRegion": "us-east-1"
    }
  ]
}

If I execute the same code with a integer in the timestamp it works fine:

{
  "Records": [
    {
      "kinesis": {
        "partitionKey": "partitionKey-03",
        "kinesisSchemaVersion": "1.0",
        "data": "eyJpZCI6MjY2MjU4NSwiZGVzY3JpcHRpb24iOiJUZXN0IFBheWxvYWQifQ==",
        "sequenceNumber": "49545115243490985018280067714973144582180062593244200961",
        "approximateArrivalTimestamp": 11234432432
      },
      "eventSource": "aws:kinesis",
      "eventID": "shardId-000000000000:49545115243490985018280067714973144582180062593244200961",
      "invokeIdentityArn": "arn:aws:iam::EXAMPLE",
      "eventVersion": "1.0",
      "eventName": "aws:kinesis:record",
      "eventSourceARN": "arn:aws:kinesis:EXAMPLE",
      "awsRegion": "us-east-1"
    }
  ]
}

Here is the full error log:

java.lang.IllegalArgumentException: Cannot deserialize instance of `java.util.Date` out of VALUE_NUMBER_FLOAT token
 at [Source: UNKNOWN; line: -1, column: -1] (through reference chain: com.amazonaws.services.lambda.runtime.events.KinesisEvent["Records"]->java.util.ArrayList[0]->com.amazonaws.services.lambda.runtime.events.KinesisEvent$KinesisEventRecord["kinesis"]->com.amazonaws.services.lambda.runtime.events.KinesisEvent$Record["approximateArrivalTimestamp"])
    at com.fasterxml.jackson.databind.ObjectMapper._convert(ObjectMapper.java:3922)
    at com.fasterxml.jackson.databind.ObjectMapper.convertValue(ObjectMapper.java:3853)
    at util.JsonUtil.convertToKinesisEvent(JsonUtil.java:28)
    at pojos.RecordsHandler.getMessages(RecordsHandler.java:38)
    at handler.App.handleRequest(App.java:41)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.lang.reflect.Method.invoke(Method.java:498)
    at lambdainternal.EventHandlerLoader$PojoMethodRequestHandler.handleRequest(EventHandlerLoader.java:261)
    at lambdainternal.EventHandlerLoader$PojoHandlerAsStreamHandler.handleRequest(EventHandlerLoader.java:178)
    at lambdainternal.EventHandlerLoader$2.call(EventHandlerLoader.java:906)
    at lambdainternal.AWSLambda.startRuntime(AWSLambda.java:341)
    at lambdainternal.AWSLambda.<clinit>(AWSLambda.java:63)
    at java.lang.Class.forName0(Native Method)
    at java.lang.Class.forName(Class.java:348)
    at lambdainternal.LambdaRTEntry.main(LambdaRTEntry.java:114)
Caused by: com.fasterxml.jackson.databind.exc.MismatchedInputException: Cannot deserialize instance of `java.util.Date` out of VALUE_NUMBER_FLOAT token

Does someone know what is happening?

–Update– Dependencies:

<dependencies>
    <dependency>
        <groupId>com.fasterxml.jackson.core</groupId>
        <artifactId>jackson-databind</artifactId>
        <version>2.10.1</version>
    </dependency>
    <dependency>
        <groupId>com.amazonaws</groupId>
        <artifactId>aws-lambda-java-core</artifactId>
        <version>1.2.0</version>
    </dependency>
    <dependency>
        <groupId>junit</groupId>
        <artifactId>junit</artifactId>
        <version>4.12</version>
        <scope>test</scope>
    </dependency>
    <dependency>
        <groupId>org.slf4j</groupId>
        <artifactId>slf4j-api</artifactId>
        <version>1.7.5</version>
    </dependency>
    <dependency>
        <groupId>org.slf4j</groupId>
        <artifactId>slf4j-log4j12</artifactId>
        <version>1.7.5</version>
    </dependency>
    <dependency>
        <groupId>org.projectlombok</groupId>
        <artifactId>lombok</artifactId>
        <version>1.18.10</version>
        <scope>provided</scope>
    </dependency>
    <!-- https://mvnrepository.com/artifact/com.squareup.retrofit2/retrofit -->
    <dependency>
        <groupId>com.squareup.retrofit2</groupId>
        <artifactId>retrofit</artifactId>
        <version>2.6.2</version>
    </dependency>
    <dependency>
        <groupId>com.squareup.retrofit2</groupId>
        <artifactId>converter-jackson</artifactId>
        <version>2.6.2</version>
    </dependency>
    <dependency>
        <groupId>com.fasterxml.jackson.datatype</groupId>
        <artifactId>jackson-datatype-jsr310</artifactId>
        <version>2.10.0</version>
    </dependency>
    <!-- https://mvnrepository.com/artifact/com.fasterxml.jackson.datatype/jackson-datatype-joda -->
    <dependency>
        <groupId>com.fasterxml.jackson.datatype</groupId>
        <artifactId>jackson-datatype-joda</artifactId>
        <version>2.10.1</version>
    </dependency>
    <dependency>
        <groupId>com.googlecode.json-simple</groupId>
        <artifactId>json-simple</artifactId>
        <version>1.1.1</version>
    </dependency>
    <dependency>
        <groupId>org.apache.commons</groupId>
        <artifactId>commons-collections4</artifactId>
        <version>4.1</version>
    </dependency>
    <!-- https://mvnrepository.com/artifact/com.amazonaws/aws-lambda-java-events -->
    <dependency>
        <groupId>com.amazonaws</groupId>
        <artifactId>aws-lambda-java-events</artifactId>
        <version>2.2.7</version>
    </dependency>
    <!-- https://mvnrepository.com/artifact/com.amazonaws/aws-java-sdk -->
    <dependency>
        <groupId>com.amazonaws</groupId>
        <artifactId>aws-java-sdk</artifactId>
        <version>1.11.685</version>
    </dependency>

</dependencies>

Thanks

Advertisement

Answer

Ok, so what I ended up doing was to add my custom deserializer to Jackson in order to solve the problem:

public class TimestampDeserializer extends JsonDeserializer<Date> {

    @Override
    public Date deserialize(JsonParser jsonParser, DeserializationContext deserializationContext) throws IOException {
        Calendar calendar = Calendar.getInstance();
        calendar.setTimeInMillis(jsonParser.getValueAsLong());
        return calendar.getTime();
    }
}

And then add a custom serializer

public class TimestampSerializer extends JsonSerializer<Date> {
    @Override
    public void serialize(Date date, JsonGenerator jsonGenerator, SerializerProvider serializerProvider) throws IOException {
        jsonGenerator.writeString(date.toString());
    }
}

And then add it to Jackson and it worked, I don’t know if there is another solution but It achieve my goal

ObjectMapper objectMapper = new ObjectMapper();

SimpleModule module = new SimpleModule();
module.addSerializer(Date.class, new TimestampSerializer());
module.addDeserializer(Date.class, new TimestampDeserializer());
objectMapper.registerModule(module);
User contributions licensed under: CC BY-SA
1 People found this is helpful
Advertisement