Jackson OffsetDateTime 序列化 Z 而不是 +00:00 时区?

问题描述 投票:0回答:5

我将 Spring Boot 与以下 ObjectMapper 一起使用:

@Bean
public ObjectMapper objectMapper()
{
    final ObjectMapper mapper = new ObjectMapper();

    mapper.enable(SerializationFeature.INDENT_OUTPUT);

    mapper.disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS);  
    mapper.setDateFormat(new StdDateFormat().withColonInTimeZone(true)); // Makes no difference to output

    mapper.findAndRegisterModules();

    return mapper;
}

当 OffsetDateTimes 被序列化并在响应中返回时,它们的格式如下:

"2020-02-28T12:28:29.01Z"
"2020-02-28T12:36:21.885Z"

我本来希望最后的时区信息看起来像这样:

"2020-02-28T10:41:25.287+00:00"

我在这里遗漏或做错了什么,或者无论如何我可以将时区信息序列化为

+00:00
格式而不是
885Z
格式?

非常感谢!

java json date jackson
5个回答
11
投票

以下步骤解决了这个问题(取自https://stackoverflow.com/a/41893238/12177456),也感谢@Ralf Wagner和@deHaar:

<dependency>
  <groupId>com.fasterxml.jackson.datatype</groupId>
  <artifactId>jackson-datatype-jsr310</artifactId>
  <version>2.6.5</version>
</dependency>
public class OffsetDateTimeSerializer extends JsonSerializer<OffsetDateTime>
{
    private static final DateTimeFormatter ISO_8601_FORMATTER = DateTimeFormatter
        .ofPattern("yyyy-MM-dd'T'HH:mm:ssxxx")
        .withZone(ZoneId.of("UTC"));

    @Override
    public void serialize(OffsetDateTime value, JsonGenerator jsonGenerator, SerializerProvider serializerProvider) throws IOException
    {
        if (value == null) {
            throw new IOException("OffsetDateTime argument is null.");
        }

        jsonGenerator.writeString(ISO_8601_FORMATTER.format(value));
    }
}
@Bean
public ObjectMapper objectMapper()
{

    ObjectMapper objectMapper = new ObjectMapper();
    objectMapper.enable(SerializationFeature.INDENT_OUTPUT);
    objectMapper.registerModule(new JavaTimeModule());
    SimpleModule simpleModule = new SimpleModule();

    simpleModule.addSerializer(OffsetDateTime.class, new OffsetDateTimeSerializer());
    objectMapper.registerModule(simpleModule);

    return objectMapper;
}

8
投票

新的 Java 8 Time API 提供了一个

DateTimeFormatter
,您可以在其中将格式结尾设置为一个或多个
x
X
。根据 api 描述:

偏移量 X 和 x:根据模式字母的数量格式化偏移量。一个字母仅输出小时,例如“+01”,除非分钟非零,在这种情况下也会输出分钟,例如“+0130”。两个字母输出小时和分钟,不带冒号,例如“+0130”。三个字母输出小时和分钟,带有冒号,例如“+01:30”。四个字母输出小时和分钟以及可选的秒,不带冒号,例如“+013015”。五个字母输出小时和分钟以及可选的秒,带有冒号,例如“+01:30:15”。六个或更多字母将引发 IllegalArgumentException。当要输出的偏移量为零时,模式字母“X”(大写)将输出“Z”,而模式字母“x”(小写)将输出“+00”、“+0000”或“+00” :00'.

因此,在您的情况下,您的格式化字符串应以

xxx
结尾,代表
+1:30
,例如
"yyyy-MM-dd'T'HH:mm:ss.SSSxxx"
SSS
始终给出毫秒。

要将其与 Jackson 一起使用

DateTimeFormatter
,您需要定义一个自定义序列化器

public class DefaultZonedDateTimeSerializer extends JsonSerializer<ZonedDateTime> {


  private static final DateTimeFormatter ISO_8601_FORMATTER = DateTimeFormatter
        .ofPattern("yyyy-MM-dd'T'HH:mm:ss.SSSXXX")
        .withZone(ZoneId.of("UTC"));

  @Override
  public void serialize(ZonedDateTime value, JsonGenerator gen, SerializerProvider serializers) throws IOException {
    if (value == null) {
        throw new IOException("ZonedDateTime argument is null.");
    }

    gen.writeString(ISO_8601_FORMATTER.format(value));
}

并用

注释 Bean 中的相应字段
@JsonSerialize(using = DefaultZonedDateTimeSerializer.class)
private ZonedDateTime someTimeProperty;

或者您需要从

DateTimeFormatter
转换为
DateFormat
(较旧,但由 Jackson 使用),如下所述:将 DateTimeFormatter 与 ObjectMapper 一起使用


6
投票

有多种使用预构建格式或向

DateTimeFormatter
提供自定义模式的可能性。

看看这些(非常简单)的例子:

public static void main(String[] arguments) {
    Instant now = Instant.now();
    
    ZonedDateTime zonedDateTime = ZonedDateTime.ofInstant(now, ZoneId.of("UTC"));
    OffsetDateTime offsetDateTime = OffsetDateTime.ofInstant(now, ZoneId.of("UTC"));
    
    System.out.println(zonedDateTime.toString());
    System.out.println(offsetDateTime.toString());
    System.out.println(zonedDateTime.format(DateTimeFormatter.ISO_ZONED_DATE_TIME));
    System.out.println(offsetDateTime.format(DateTimeFormatter.ISO_OFFSET_DATE_TIME));
    System.out.println(zonedDateTime.format(
            DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ssZ"))
    );
    System.out.println(offsetDateTime.format(
            DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ssxxx")
        )
    );
}

我认为你期待的是最后一个,一种以

xxx
结尾的模式,这会导致偏移量始终以
HH:mm
的形式显示,代码示例的输出是:

2020-02-28T12:49:02.388Z[UTC]
2020-02-28T12:49:02.388Z
2020-02-28T12:49:02.388Z[UTC]
2020-02-28T12:49:02.388Z
2020-02-28T12:49:02+0000
2020-02-28T12:49:02+00:00

0
投票

如果您使用 JodaDateTime,请尝试以下操作:

    @Bean
    public ObjectMapper getObjectMapper() {
        ObjectMapper objectMapper = new ObjectMapper();
        objectMapper.registerModule(new JodaModule());
        objectMapper.configure(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS, false);
        return objectMapper;
    }

0
投票

感谢

@Ralf Wagner
answer,它引用的API描述很有帮助。

我在我的 Spring Boot 项目中这样做了,它工作正常:

application.yaml
spring:
  jackson:
    zoned-date-time-date-format: "yyyy-MM-dd'T'HH:mm:ssxxx" # This is a custom key
JacksonConfig.java
import com.fasterxml.jackson.datatype.jsr310.ser.ZonedDateTimeSerializer;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.autoconfigure.jackson.Jackson2ObjectMapperBuilderCustomizer;
import org.springframework.boot.jackson.JsonComponent;
import org.springframework.http.converter.json.Jackson2ObjectMapperBuilder;

import java.time.format.DateTimeFormatter;

@SuppressWarnings("unused")
@JsonComponent
public class JacksonConfig implements Jackson2ObjectMapperBuilderCustomizer {

    @Value("${spring.jackson.zoned-date-time-date-format:}")
    protected String dateFormat;

    @Override
    public void customize(Jackson2ObjectMapperBuilder builder) {
        // If the "spring.jackson.zoned-date-time-date-format" item exists in application.yaml
        if (!dateFormat.isBlank()) {
            // Configure the date and time format used when serializing Java 8 time API classes
            var dateTimeFormatter = DateTimeFormatter.ofPattern(dateFormat);
            builder.serializers(new ZonedDateTimeSerializer(dateTimeFormatter));
        }
    }

}
© www.soinside.com 2019 - 2024. All rights reserved.