microsoft / durabletask-java

Java SDK for Durable Functions and the Durable Task Framework
MIT License
13 stars 7 forks source link

Deserialize problem on LocalDate #134

Open nicktombeur opened 1 year ago

nicktombeur commented 1 year ago

When returning an object containing a LocalDate, I get the following error.

Caused by: com.fasterxml.jackson.databind.exc.MismatchedInputException: Cannot deserialize value of type `java.lang.String` from Object value (token `JsonToken.START_OBJECT`)
 at [Source: (String)"{"date":{"year":2023,"month":5,"day":13},"value":"foo"}"; line: 1, column: 1] 
...
 at com.microsoft.durabletask.JacksonDataConverter.deserialize(JacksonDataConverter.java:40)

I believe this date format is coming from protobuf? I can solve this problem by writing my own custom Jackson deserializer but shouldn't this be handled for us? Maybe it's possible to return{ "date": "2023-05-13" } instead? This way I can add jackson-datatype-jsr310 to the class path and com.microsoft.durabletask.JacksonDataConverter will pick it up.

Example code:

public class ExampleFunction {

    @FunctionName("StartOrchestration")
    public HttpResponseMessage startOrchestration(
            @HttpTrigger(name = "req",
                    methods = {HttpMethod.GET, HttpMethod.POST},
                    authLevel = AuthorizationLevel.ANONYMOUS) final HttpRequestMessage<Optional<String>> request,
            @DurableClientInput(name = "durableContext") final DurableClientContext durableContext,
            final ExecutionContext context) {
        context.getLogger().info("Java HTTP trigger processed a request");

        final DurableTaskClient client = durableContext.getClient();
        final String instanceId = client.scheduleNewOrchestrationInstance("ExampleProcess");
        return durableContext.createCheckStatusResponse(request, instanceId);
    }

    @FunctionName("ExampleProcess")
    public String exampleOrchestrator(
            @DurableOrchestrationTrigger(name = "taskOrchestrationContext") final TaskOrchestrationContext context,
            final ExecutionContext functionContext) {
        return context.callActivity("ToLower", "Foo", String.class).await() +
                    " " +
                    context.callActivity("ToLower", "Bar", String.class).await();
    }

    @FunctionName("ToLower")
    public ExampleResponse toLower(@DurableActivityTrigger(name = "value") final String value, final ExecutionContext context) {
        return new ExampleResponse(LocalDate.now(), value.toLowerCase());
    }
}

public class ExampleResponse {
    private LocalDate date;
    private String value;

   ...
}
kaibocai commented 1 year ago

Maybe it's possible to return { "date": "2023-05-13" } instead? This way I can add jackson-datatype-jsr310 to the classpath and com.microsoft.durabletask.JacksonDataConverter will pick it up.

Hi @nicktombeur, thanks for reaching out with this issue. I think there are few reasons that causing this issue for you.

  1. Jackson itself issue with serializing/deserializing java.time.LocalDate. As you pointed out, it could be resolved by adding add jackson-datatype-jsr310 to your classpath and registering the JavaTimeModule. However, we don't expose any interface to customers for example to register modules in ObjectMapper of jackson. So even if you could add add jackson-datatype-jsr310 to your classpath, I don't think there is a way for you to register the module.
  2. the date is serialized as "date":{"year":2023,"month":6,"day":20} instead of { "date": "2023-05-13" }, this is caused by the different formatting between gson used in Azure Function Java Language Worker and jackson used in this repo.

For now please use your own custom Jackson deserializer as a workaround. The team will try to improve this soon. Thanks.