2

I have a spring rest service that accepts and gives json output.

@PostMapping(path = "/path", consumes = {"application/json"}, produces = {"application/json"})
public ResponseEntity<RequestData> method(@RequestBody RequestData request){
    return request;
}

RequestData contains several dates (XMLGregorianCalendar). I cannot change the type, since it is generated from xsd. To get dates with the original time zones, I used the parameter spring.jackson.date-format: yyyy-MM-dd'T'HH:mm:ssZ

Request

{
    "date1":"2020-02-28T09:26:59+09:00",
    "date2":"2020-01-10T12:46:29+04:00",
    "date3":"2020-03-15T11:32:43+08:00"
}

From this request, I got an XMLGregorianCalendar with different time zones. But when sending a response message, the dates are converted to 0 time zone. Response

{
    "date1":"2020-02-28T00:26:59+0000",
    "date2":"2020-01-10T08:46:29+0000",
    "date3":"2020-03-15T03:32:43+0000"
}

What settings need to be done on jackson to get non-zero time zones in the response? It is necessary that the response time zones returned in the request. Or maybe jackson does not know how to do this and always converts the date to a single time zone? In that case, which library to use? Thanks!


Solution

You must create a serializer and deserializer. Then you need to override the existing ObjectMapper. If only the serializer is overrided, then upon receipt of the data, the time zone will be normalized (reduced to +00:00), therefore it is also necessary to override the deserializer. Serializer:

public class XMLGCSerializer extends JsonSerializer<XMLGregorianCalendar> {

    @Override
    public void serialize(XMLGregorianCalendar value,
                          JsonGenerator gen,
                          SerializerProvider serializers)
            throws IOException {
        gen.writeObject(value.toString());
    }
}

Deserializer:

public class XMLGCDeserializer extends JsonDeserializer<XMLGregorianCalendar> {

    @Override
    public XMLGregorianCalendar deserialize(JsonParser parser, DeserializationContext context) throws IOException {
        String stringDate = parser.getText();
        try {
            return DatatypeFactory.newInstance().newXMLGregorianCalendar(stringDate);
        } catch (Exception e) {
            throw new RuntimeException(e);
            //or return null
        }
    }
}

Override ObjectMapper

@Component
public class JacksonConfig {

    private final ObjectMapper objectMapper;

    public JacksonConfig() {
        objectMapper = new ObjectMapper();
        SimpleModule s = new SimpleModule();
        s.addSerializer(XMLGregorianCalendar.class, new XMLGCSerializer());
        s.addDeserializer(XMLGregorianCalendar.class, new XMLGCDeserializer());
        objectMapper.registerModule(s);
    }

    @Bean
    public ObjectMapper getContext() {
        return objectMapper;
    }
}
  • That's two different problems in one. 1st: You lose the timezone information during deserialization. 2nd: You want to enforce the set timezone in the dateformat for serialization. Both is troublesome with XMLGregorianCalendar, and seems to be an ongoing issue: https://github.com/FasterXML/jackson-databind/issues/1791 – kasoban Mar 19 '20 at 09:18
  • Yes that's right. The first problem is solved by adding a parameter to the configuration spring.jackson.date-format: yyyy-MM-dd'T'HH: mm: ssZ After that, the data is deserialized correctly and the correct time zone is present in the XMLGregorianCalendar. But the second problem can not be solved. – Руслан Кузнецов Mar 19 '20 at 09:43
  • @kasoban Info isn't lost during deserialization but serialization. – Aniket Sahrawat Mar 19 '20 at 10:06
  • I'd argue there is no information lost: `2020-02-28T09:26:59+09:00` (request) is the same point in time as `2020-02-28T00:26:59+0000` (response). Why does the client bother with particular timezones as long as the point in time is correct? – Ralf Mar 19 '20 at 10:51
  • @Ralf I understand what you mean but there might be a situation where timezone is required by the client, we never know. – Aniket Sahrawat Mar 19 '20 at 11:19
  • But the timezone is there, is it not? It is `+0000`, which is Zulu time. So it is clear what the point in time is and a client application may shift it to any timezone (for display to a user?) it needs it to be. – Ralf Mar 19 '20 at 16:25
  • @Ralf I disagree. Yes, the point in time is identical and shouldn't make a functional difference, but before deserializing the information was there that the client sent a date in the +09:00 timezone offset. Afterwards, we only know the point in time. If for whatever reason we want to introduce special logic for dates sent in a specific timezone, we couldn't anymore. – kasoban Mar 20 '20 at 08:31
  • @kasoban, time and location are two different concepts. IMO if location is important for business functionality then the required information should be provided separately. – Ralf Mar 20 '20 at 09:54

1 Answers1

1

You can create a seperate class to handle serialization by yourself. Here is an example:

class XMLGCSerializer extends JsonSerializer<XMLGregorianCalendar> {

    @Override
    public void serialize(XMLGregorianCalendar value, 
            JsonGenerator gen, 
            SerializerProvider serializers)
            throws IOException {
        gen.writeObject(value.toString());
    }

}

Now you just need to annotate your fields in RequestData:

class RequestData{
    @JsonSerialize(using = XMLGCSerializer.class)
    XMLGregorianCalendar date1;
    //...
}
Aniket Sahrawat
  • 12,410
  • 3
  • 41
  • 67