Unable to deserialize result from projection in Java

Hi all,

I’m having an issue in deserializing the result of a projection in EventStoreDB. I’m crushing my head with this issue, without finding a solution and I hope you can help me.
I’m using EventStoreDB 24.6 with Java client 5.4.1.
I created a LessonProjection projection and I’m trying to read the result of this projection.

This is the result of the projection that I can see in the EventStoreDB UI:

{
  "lessons": [
    {
      "id": "679f5b1b-ab70-42e1-9c43-c2ffb5555184",
      "date": [
        2024,
        8,
        31
      ],
      "startTime": [
        19,
        30
      ],
      "endTime": [
        20,
        30
      ],
      "maxNumberAttenders": 10,
      "status": "open"
    }
  ]
}

This Json should be deserialized using the following class:

public class LessonProjectionResult {
    public ArrayList<Lesson> lessons;
}
public class Lesson {
    public enum Status { OPEN, CANCELED }

    private UUID id;

    private LocalDate date;

    private LocalTime startTime;

    private LocalTime endTime;

    private int maxNumberAttenders;

    private Status status;

    public void setId(UUID id) { this.id = id; }

    @JsonDeserialize(using = LocalDateDeserializer.class)
    public void setDate(LocalDate date) { this.date = date; }

    @JsonDeserialize(using = LocalTimeDeserializer.class)
    public void setStartTime(LocalTime startTime) { this.startTime = startTime; }

    @JsonDeserialize(using = LocalTimeDeserializer.class)
    public void setEndTime(LocalTime endTime) { this.endTime = endTime; }

    public void setMaxNumberAttenders(int maxNumberAttenders) { this.maxNumberAttenders = maxNumberAttenders; }

    public void setStatus(Status status) { this.status = status; }
}

If I deserialize the Json starting from a string then it works. Something like this:

        var jsonString = "{\n" +
                "  \"lessons\": [\n" +
                "    {\n" +
                "      \"id\": \"679f5b1b-ab70-42e1-9c43-c2ffb5555184\",\n" +
                "      \"date\": [\n" +
                "        2024,\n" +
                "        8,\n" +
                "        31\n" +
                "      ],\n" +
                "      \"startTime\": [\n" +
                "        19,\n" +
                "        30\n" +
                "      ],\n" +
                "      \"endTime\": [\n" +
                "        20,\n" +
                "        30\n" +
                "      ],\n" +
                "      \"maxNumberAttenders\": 10,\n" +
                "      \"status\": \"OPEN\"\n" +
                "    }\n" +
                "  ]\n" +
                "}";

        var objectiMapper = new ObjectMapper();
        var lesson = objectiMapper.readValue(jsonString, LessonProjectionResult.class);

But if I try to retrieve and parse the same Json coming from EventStoreDB then I get an exception:

Exception in thread "main" java.util.concurrent.ExecutionException: com.fasterxml.jackson.databind.exc.MismatchedInputException: Unexpected token (VALUE_NUMBER_FLOAT) within Array, expected VALUE_NUMBER_INT
 at [Source: REDACTED (`StreamReadFeature.INCLUDE_SOURCE_IN_LOCATION` disabled); line: 4, column: 14] (through reference chain: LessonProjectionResult["lessons"]->java.util.ArrayList[0]->Lesson["date"])
	at java.base/java.util.concurrent.CompletableFuture.reportGet(CompletableFuture.java:396)
	at java.base/java.util.concurrent.CompletableFuture.get(CompletableFuture.java:2073)
	at JsonTest.ReadJsonFromProjection(JsonTest.java:54)
	at JsonTest.main(JsonTest.java:39)
Caused by: com.fasterxml.jackson.databind.exc.MismatchedInputException: Unexpected token (VALUE_NUMBER_FLOAT) within Array, expected VALUE_NUMBER_INT
 at [Source: REDACTED (`StreamReadFeature.INCLUDE_SOURCE_IN_LOCATION` disabled); line: 4, column: 14] (through reference chain: LessonProjectionResult["lessons"]->java.util.ArrayList[0]->Lesson["date"])

This is the code that generate the exception:

String connectionString = "esdb://admin:changeit@localhost:2113?tls=false&tlsVerifyCert=false";
var settings = EventStoreDBConnectionString.parseOrThrow(connectionString);
var client = EventStoreDBProjectionManagementClient.create(settings);
var result = client.getResult("LessonProjection", LessonProjectionResult.class).get();

I’m sure there is something I’m not seeing. Any advice?

Thank you.

Hi,

I found a workaround: specify a JsonFormatter pattern for the LocalDate and LocalTime, to avoid the serialization of these two types of data in arrays of integers.

To serialize:

          @JsonSerialize(using = LocalDateSerializer.class)
          @JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "dd/MM/yyyy")
          @Override
          public LocalDate date() {
               return date;
          }

          @JsonSerialize(using = LocalTimeSerializer.class)
          @JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "HH:mm")
          @Override
          public LocalTime endTime() {
               return endTime;
          }

          @JsonSerialize(using = LocalTimeSerializer.class)
          @JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "HH:mm")
          @Override
          public LocalTime startTime() {
               return startTime;
          }

To deserialize:

    @JsonSetter("date")
    @JsonDeserialize(using = LocalDateDeserializer.class)
    @JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "dd/MM/yyyy")
    public void setDate(LocalDate date) { this.date = date; }

    @JsonDeserialize(using = LocalTimeDeserializer.class)
    @JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "HH:mm")
    public void setStartTime(LocalTime startTime) { this.startTime = startTime; }

    @JsonDeserialize(using = LocalTimeDeserializer.class)
    @JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "HH:mm")
    public void setEndTime(LocalTime endTime) { this.endTime = endTime; }

ah thanks for the update

Indeed default json (de)serialization format in most parser in any stack expect an ISO 8601 date format
( YYYY-MM-DDTHH:mm:ss.sssZ or ±YYYYYY-MM-DDTHH:mm:ss.sssZ)

(the JSON spec itself does not require it, and says nothing about date & time format )