0

I have somewhat of a strange issue here with date variables in Kotlin/Spring. I have a service of two applications. One "logical" backend, and a pure database API service handling database insertions. The logical backend is written in Kotlin and the database service runs in Node. I use WebClient to request the database service, which is configured as a bean to be shared between multiple services.

The issue is regarding deserializing a date with (UTC) timezone from the database service. Serializing the date when passing it to the database API works just as expected, but when it receives the same data it once serialized it returns this error.

java.time.format.DateTimeParseException: Text '2021-03-10T12:45:00.000Z' could not be parsed at index 23
    at java.base/java.time.format.DateTimeFormatter.parseResolved0(DateTimeFormatter.java:2046) ~[na:na]
    at java.base/java.time.format.DateTimeFormatter.parse(DateTimeFormatter.java:1874) ~[na:na]
    at com.fasterxml.jackson.datatype.jsr310.deser.InstantDeserializer._fromString(InstantDeserializer.java:282) ~[jackson-datatype-jsr310-2.12.2.jar:2.12.2]
    ...

If I've understood correctly, it has some issue with the time zone (index 23 equals the Z). But then I don't understand why it can serialize without any issue?

Request from backend:

val students = webClient
            .get()
            .uri("/students")
            .retrieve()
            .bodyToMono(Array<Student>::class.java)
            .block(Duration.ofSeconds(ApiConstants.REQUEST_TIMEOUT))!!

I've applied a JsonFormat-annotation to make it work one way, but the other doesn't work.

    @JsonFormat(pattern = "yyyy-MM-dd'T'HH:mm:ss.SSSX", shape = JsonFormat.Shape.STRING)
    val date: ZonedDateTime = ZonedDateTime.now(),

I've also tried to get the pure JSON string and parse it with jacksonObjectMapper, but with the same result. Where I also applied the JavaTimeModule with no result.

I also tried to create a custom deserializer and annotating the ZonedDateTime field with JsonSerialize and Deserialize, following this example, but that only results in another error. Which starts making me wonder if Jackson is the issue here.

java.lang.NoSuchMethodError: 'boolean com.fasterxml.jackson.databind.SerializationConfig.hasExplicitTimeZone()'
    at com.fasterxml.jackson.datatype.jsr310.ser.InstantSerializerBase.formatValue(InstantSerializerBase.java:144) ~[jackson-datatype-jsr310-2.12.2.jar:2.12.2]
    at com.fasterxml.jackson.datatype.jsr310.ser.InstantSerializerBase.serialize(InstantSerializerBase.java:103) ~[jackson-datatype-jsr310-2.12.2.jar:2.12.2]
    ...

Update

So, the issue is at Jackson. Since the backend manages to read the value itself without any issue, it just won't create the JSON output in a good way.

So, I found a solution. Albeit not a good-looking one. The logic here is that I let Spring/Jackson read into the variable as it wants and I only allow Jackson to write to it, and then I have a separate getter method that creates the missing field in the JSON output.

But I would really like to solve the original problem still, since this solution obviously is more error prone to changes down the line.

@JsonIgnoreProperties(ignoreUnknown = true)
data class Order(
    @JsonProperty(access = JsonProperty.Access.WRITE_ONLY)
    val date: ZonedDateTime = ZonedDateTime.now(),
) {
    val outputDate: ZonedDateTime
        @JsonFormat(pattern = "yyyy-MM-dd'T'HH:mm:ss.SSSX", shape = JsonFormat.Shape.STRING)
        @JsonProperty("date")
        get() = this.date
}
Terris
  • 25
  • 8
  • have you added the date/time support module? https://github.com/FasterXML/jackson-modules-java8 – Toerktumlare Mar 11 '21 at 10:30
  • Yes, I have added jackson-datatype-jsr310 as a maven dependency, which should be enough from my understanding? – Terris Mar 11 '21 at 10:57
  • no you also need to register the module with the objectmapper, check my link the bottom part. – Toerktumlare Mar 11 '21 at 11:11
  • That's what I tried earlier when I used jacksonObjectMapper instead of letting WebClient do the serialization. "I've also tried to get the pure JSON string and parse it with jacksonObjectMapper, but with the same result. Where I also applied the JavaTimeModule with no result." – Terris Mar 11 '21 at 11:18
  • why the `yyyy-MM-dd'T'HH:mm:ss.SSSX` X on the end, shouldn't it be a Z? – Toerktumlare Mar 11 '21 at 11:26
  • Ah, then I'm following you. But in that case, shouldn't this snippet work as well? It still brings up the latest error in the main post, where hasExplicitTimeZone() is not defined as a method (if I understood it correctly). Where it fails on readValue. [Example](https://codeshare.io/aYvreY) – Terris Mar 11 '21 at 11:28
  • I swapped from X to Z after [this comment](https://stackoverflow.com/a/54349469/4102226) after searching around trying to fix the issue. X gives me an error on `hasExplicitTimeZone()`, Z gives me an error on the Z (parsing the time zone), ref the first error from the post. – Terris Mar 11 '21 at 11:36
  • what versions are you running, java version, jackson version, module version etc – Toerktumlare Mar 11 '21 at 11:43
  • Spring version 2.4.1 | Java version 11 | Kotlin version 1.4.21 | Jackson (and modules) are bundled in Spring, and has version 2.11.3, dependencies are [here](https://docs.spring.io/spring-boot/docs/2.4.1/reference/html/appendix-dependency-versions.html#dependency-versions) – Terris Mar 11 '21 at 11:58
  • but if you run mvn dependency::tree – Toerktumlare Mar 11 '21 at 12:09
  • 2.11.3, everything related to jackson. – Terris Mar 11 '21 at 12:17
  • Also forgot to mention. If I remove the @JsonFormat-annotation, it does "work". Then the json looks like this: `"date": 1615380300.000000000`, which is unusable for the (not yet developed) frontend, but inside the backend the value is "correct". It is saved as a ZonedDateTime with both the offset "Z" and zone "UTC". – Terris Mar 11 '21 at 12:21
  • best would if you can produce a small reproducible example that we can try out. – Toerktumlare Mar 11 '21 at 20:56
  • It's fine. I'm satisfied with my own solution so I'll just keep it as is. But I appreciate your help nonetheless. :) – Terris Mar 12 '21 at 08:03

0 Answers0