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
}