5

Problem: When our REST service returns objects with date/datetime fields, these fields are encoded as complex structures in the JSON format:

    ...
    "createdTimestamp": {
        "offset": {
            "totalSeconds": 0,
            "id": "Z",
            "rules": {
                "fixedOffset": true,
                "transitionRules": [],
                "transitions": []
            }
        },
        "nano": 257353000,
        "hour": 16,
        "minute": 5,
        "second": 25,
        "month": "OCTOBER",
        "year": 2017,
        "dayOfMonth": 5,
        "dayOfWeek": "THURSDAY",
        "dayOfYear": 278,
        "monthValue": 10
    },
    ...

I want these temporal data types to be serialized to string in a simple format like "2017-10-05T16:05:25".

The project was originally based on Java 7 API and java.util.Date was used everywhere – and it worked. When I switched to Java 8 and started using the new java.time classes, everything worked except for the serialization of the date/time objects to JSON.

I know I could revert to using the java.util.Date type again but I would prefer using the new data types.

The REST service is defined using Swagger and built with Maven. The swagger-codegen-maven-plugin is used, with the following configuration:

  • plugin version: 2.2.3
  • language: jaxrs-cxf-cdi
  • dateLibrary: java8

Here are versions of other related libraries (as defined in the POM):

  • swagger-codegen: 2.2.3
  • cxf-bundle-jaxrs: 2.7.18
  • gson: 2.8.1

I'm quite new to REST services (I've only worked with SOAP services until recently).

I've tried to find a way of customizing the output of serialization to JSON but didn't succeed. I found out that OffsetDateTime should be a supported type (regarding serialization) and should be serialized nicely. I have also tried to configure the Maven plugin to use Java library 'java8-localdatetime' with type LocalDateTime – but it didn't help – the serialized instance of LocalDateTime was still converted as a complex JSON object.

Could you help me with this? Thanks.

  • It seems it's not Swagger what performs JSON processing. It's one of the libraries (e.g. Jackson), that is either bundled in the application server or in the application. – Marián Petráš Oct 07 '17 at 11:48
  • Does this answer your question? [Spring Data JPA - ZonedDateTime format for json serialization](https://stackoverflow.com/questions/31627992/spring-data-jpa-zoneddatetime-format-for-json-serialization) – Michael Gantman Feb 11 '20 at 15:47

2 Answers2

1

I had the same issue today, my approach was to follow the dependencies starting with the generated ApiClient:

private DateTimeFormatter dtf = DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ss.SSSZ");

// create a message converter for jackson using a prepared objectMapper
ObjectMapper mapper = objectMapper();
MappingJackson2HttpMessageConverter converter = new MappingJackson2HttpMessageConverter(mapper);
rest = new RestTemplate();
// add the converter at first position to converter list of the rest template
rest.getMessageConverters().add(0, converter );

// finally create the ApiClient with the configured restTemplate
ApiClient defaultClient = new ApiClient(rest);



    public ObjectMapper objectMapper() {
        ObjectMapper objectMapper = new ObjectMapper();
        objectMapper.findAndRegisterModules();
        JavaTimeModule module = new JavaTimeModule();

        module.addDeserializer(OffsetDateTime.class, new StdDeserializer<OffsetDateTime>(OffsetDateTime.class) {

            @Override
            public OffsetDateTime deserialize(JsonParser p, DeserializationContext ctxt)
                    throws IOException {
                return OffsetDateTime.parse(p.getText());
            }
        });
        module.addSerializer(OffsetDateTime.class, new StdSerializer<OffsetDateTime>(OffsetDateTime.class) {

            @Override
            public void serialize( OffsetDateTime value, JsonGenerator gen, SerializerProvider provider) throws IOException {
                gen.writeString( dtf.format( value ));
            }
        });
        // possible alternative to custom serializer if default format is sufficient
        //objectMapper.disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS);
        objectMapper.registerModule(module);
        return objectMapper;
    }

Used configuration:

  1. swagger-codegen-cli-3.0.14.jar -l java --library resttemplate

  2. Options in config.json "dateLibrary":"java8" (creates OffsetDateTime in client code)

  3. Jackson-Version 2.10.2

Maven Dependencies:

<dependency>
    <groupId>com.fasterxml.jackson.datatype</groupId>
    <artifactId>jackson-datatype-jdk8</artifactId>
</dependency>
<dependency>
    <groupId>com.fasterxml.jackson.datatype</groupId>
    <artifactId>jackson-datatype-jsr310</artifactId>
</dependency>
<dependency>
    <groupId>com.fasterxml.jackson.core</groupId>
    <artifactId>jackson-databind</artifactId>
</dependency>

<dependency>
    <groupId>com.fasterxml.jackson.core</groupId>
    <artifactId>jackson-core</artifactId>
</dependency>   

See also:

stacker
  • 68,052
  • 28
  • 140
  • 210
0

A simple answer would be to write a class that would be a returned value of your REST service that would look like this:

public class MyDto {
    ZonedDateTime time = ZonedDateTime.now();

    @JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "yyyy-MM-dd'T'HH:mm:ss.SSSZ")
    public ZonedDateTime getTime() {
        return time;
    }

Detailed info is in this question and answer: Spring Data JPA - ZonedDateTime format for json serialization

Michael Gantman
  • 7,315
  • 2
  • 19
  • 36