Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

@JsonFormat annotation doesn't work for OffsetDateTime when configuring Global OffsetDateTimeSerializer #254

Closed
yuzmann opened this issue Dec 2, 2022 · 4 comments

Comments

@yuzmann
Copy link

yuzmann commented Dec 2, 2022

After configuring ObjectMapper globally and with annotation, serialization works expectedly on Date and LocalDateTime types.

As for OffsetDateTime, @jsonformat annotation doesn't work and mapper always use Global config.

In order to solve the issue, I need to use custom serializer and @JsonSerialize annotation as below,
@JsonSerialize(using = OffsetDateTimeSerializerImpl.class) private OffsetDateTime offsetDateTimeAnnotated;

Note: JsonMapper has the same problem with ObjectMapper

Maven:

<dependency>
  <groupId>com.fasterxml.jackson.datatype</groupId>
  <artifactId>jackson-datatype-jsr310</artifactId>
  <version>2.3.14</version>
</dependency>

Formatters:

For Global ('G' between date and time)
Date: yyyy-MM-dd'G'HH:mm:ss
LocalDateTime: yyyy-MM-dd'G'HH:mm:ss.SSS
OffsetDateTime: yyyy-MM-dd'G'HH:mm:ssZ

For Annotation ('A' between date and time)
Date: yyyy-MM-dd'A'HH:mm:ss
LocalDateTime: yyyy-MM-dd'A'HH:mm:ss.SSS
OffsetDateTime: yyyy-MM-dd'A'HH:mm:ssZ

Result:
{
"date" : "2022-12-02G17:29:02",
"dateAnnotated" : "2022-12-02A17:29:02",
"localDateTime" : "2022-12-02G17:29:02.194",
"localDateTimeAnnotated" : "2022-12-02A17:29:02.194",
"offsetDateTime" : "2022-12-02G17:29:02+0300",
"offsetDateTimeAnnotated" : "2022-12-02G17:29:02+0300" <-- It must be in format: "yyyy-MM-dd'A'HH:mm:ssZ"
}

   @Bean
    public ObjectMapper objectMapper() {
        ObjectMapper mapper = new ObjectMapper()
                .registerModule(new JavaTimeModule())
                .disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS);

        // for Date
        mapper.setDateFormat("yyyy-MM-dd-HH:mm:ss");
        mapper.setTimeZone(TimeZone.getDefault());

        // for LocalDateTime
        SimpleModule simpleModule = new SimpleModule();
        simpleModule.addSerializer(new LocalDateTimeSerializer(DateTimeFormatter.ofPattern("yyyy-MM-dd'G'HH:mm:ss.SSS")));

        // for OffsetDateTime

        // with serializer:
        // -  @JsonFormat doesn't work

        // without serializer:
        // -  @JsonFormat works
        // -  but no control over global format, it becomes ISO_OFFSET_DATE_TIME

       simpleModule.addSerializer(OffsetDateTime.class, new JsonSerializer<OffsetDateTime>() {
            @Override
            public void serialize(OffsetDateTime offsetDateTime, JsonGenerator jsonGenerator, SerializerProvider serializerProvider) throws IOException, JsonProcessingException {
                jsonGenerator.writeString(DateTimeFormatter.ofPattern("yyyy-MM-dd'G'HH:mm:ssZ").format(offsetDateTime));
            }
        });

        mapper.registerModule(simpleModule);
        return mapper;
    }
public class DateFormats {

    private Date date;

    @JsonFormat(pattern="yyyy-MM-dd'A'HH:mm:ss")
    private Date dateAnnotated;

    private LocalDateTime localDateTime;

    @JsonFormat(pattern="yyyy-MM-dd'A'HH:mm:ss.SSS")
    private LocalDateTime localDateTimeAnnotated;

    private OffsetDateTime offsetDateTime;

    @JsonFormat(pattern="yyyy-MM-dd'A'HH:mm:ssZ")
     private OffsetDateTime offsetDateTimeAnnotated;

    public DateFormats() {

        this.dateAnnotated = Calendar.getInstance().getTime();
        this.date = Calendar.getInstance().getTime();

        this.offsetDateTimeAnnotated = OffsetDateTime.now();
        this.offsetDateTime = OffsetDateTime.now();

        this.localDateTimeAnnotated = LocalDateTime.now();
        this.localDateTime = LocalDateTime.now();
    }

    // getter and setters
}
@cowtowncoder
Copy link
Member

I cannot reproduce the issue: use of @JsonFormat annotation and global format configuration work.

In case of custom serializer, however, you must implement all support for annotations (and global settings) yourself.
Jackson has very little control over custom implementations and so it cannot force specific format.

Note that for configuring per-type format defaults, you need to use "config overrides" like so:

        ObjectMapper m = mapperBuilder().withConfigOverride(OffsetDateTime.class,
                cfg -> cfg.setFormat(JsonFormat.Value.forPattern("yyyy.MM.dd'x'HH:mm:ss")))
            .build();

At this point I would need a unit test to reproduce the problem.

@yuzmann
Copy link
Author

yuzmann commented Dec 13, 2022

Attached is a sample spring boot project.

rest-example.zip

HealthCheckControllerIT test class can reproduce the issue.

@cowtowncoder
Copy link
Member

Unfortunately I cannot use tests that rely on external frameworks: a stand-alone example would be needed. This is due to 2 issues: first, frameworks often do their own initialization or special configuration; and second, I can't add unit tests that would require dependencies to external frameworks.

@cowtowncoder
Copy link
Member

Cannot reproduce (no stand-alone reproduction), appear to work, closing.

May be re-opened with a stand-alone reproduction against 2.14 or 2.15 branch.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

2 participants